QReferate - referate pentru educatia ta.
Cercetarile noastre - sursa ta de inspiratie! Te ajutam gratuit, documente cu imagini si grafice. Fiecare document sau comentariu il poti downloada rapid si il poti folosi pentru temele tale de acasa.



AdministratieAlimentatieArta culturaAsistenta socialaAstronomie
BiologieChimieComunicareConstructiiCosmetica
DesenDiverseDreptEconomieEngleza
FilozofieFizicaFrancezaGeografieGermana
InformaticaIstorieLatinaManagementMarketing
MatematicaMecanicaMedicinaPedagogiePsihologie
RomanaStiinte politiceTransporturiTurism
Esti aici: Qreferat » Documente informatica

Interfata asm-c. - programator-sistem



Universitatea "Al. I. Cuza" Iasi

Facultatea de Matematica

Specializarea: Matematica - Informatica







INTERFATA ASM-C.

PROGRAMATOR-SISTEM


Interfata ASM-limbaje de nivel inalt



O parte importanta a programarii in limbaj de asamblare o constitue legarea modulelor de program ASM cu module de program dezvoltate in limvaj de nivel inalt.In acest mod,se poate creste eficienta aplicatiilor,prin dezvoltarea modulelor critice(care se utilizeaza foarte des) in ASM.

In urmatoarele, va fi exemplificata dezvoltarea de aplicatii mixte in limbaj de asamblare si in limbajul C.Alegerea limbajului C ca limbaj reprezentativ de nivel inalt este justificata de urmatoarele proprietati :

raspandirea foarte mare a limbajului C,ca limbaj destinat atat programelor de sistem cat si cele aplicative ;

existenta unor medii de dezvoltare evoluate, total compatibile cu asambloarele 8086( un exemplu semnificativ fiind familia de produse Borland) ;

caracterul modular al limbajului C, prin care se permite compilarea separata a modulelor sursa ;

compatibilitatea totala a modulelor obiect(un model obiect rezultat in urma compilarii unui text sursa C are exact acelasi format ca un modul obiect rezultat al asamblarii unui text ASM).


In mod concret, interfata dintre module C si module ASM va consta din apeluri de functii ASM din C si invers, respectiv din accesul din C la date definite in ASM si invers.

Compilatorul Borland C realizeaza transferul parametrilor prin stiva, in ordinea de la dreapta la stanga a listei de parametri.Descarcarea stivei este facuta de catre modulul apelant.Intoarcerea de tipuri simple de datedin functii se realizeaza prin registrul acumulator, eventual extins( deci prin AL,AX sau DX :AX, corespunzator unui tip pe 1 , 2 sau 4 octeti).Tipurile reale ( float ,double , long double) sunt intoarse printr-o zona speciala a bibliotecii de virgula mobila sau in varful stivei coprocesorului matematic.Pentru a nu complica interfata, vom considera ca functiile C folosite in ASM nu intorc valori reale. Daca este absoluyt necesar , se poate adopta solutia ca functiile C sa intoarca pointeri la variabile reale statice.

Un alt caz complicat este transferul structurilor si al uniunilor, care este definit in C prin copierea bit cu bit a tuturor membrilor, atat la transfer spre functie, cat si la intoarcere din functie.Este si aici de preferatsolutia mult mai eficienta de a transfera sau a intoarce un pointer catre structura sau uniunea respectiva.

Numele simbolurilor externe in C ( functii si variabile externe) sunt generate implicit cu caracterul _ ( subliniere) ca prim caracter. De exemplu simbolul var este generat ca _var si vizibil ca atare intr-un modul ASM.Pentru variabile, numele este sinonim cu adresa unde va este memorata variabila.

Deoarece in C vizibilitatea externa este permisa numai variabilelor definite la nivel exterior( in afara tuturor functiilor), acestea sunt implicit alocate static, deci au adrese fixe de memorie. Daca de exemplu var este un intreg definit in C , atunci el poate fi citit in ASM printr-o instructiune de tipul mov ax,_var.

Trebuie cunoscute dimensiunile fizice ale tipurilor de baza din C.Acestea sunt : char(1 octet),int(2 octeti),short(2 octeti),long(4 octeti), float (4 octeti),double (8 octeti),long double ( 10 octeti),pointeri( 2 sau 4 octeti, functi de modelul de memorie folosit).

Trebuie tinut cont si de faptul ca operatiile cu stiva sunt operatii pe 16,deci cand se transmite un char la o functie, se pune pe stiva un cuvant de 16 biti, cu partea mai semnificativa 0.Este recomandabil ca, asemenea functiilor din biblioteca C standard, variabilele de tip char sa fie transmise si intoarse la si de la functii ca variabile de tip unsigned int, ceea ce asigura protectie la o eventuala extensie de semn la 16 biti.De exemplu , variabila char 'x81' devine prin extensie de semn , intregul 0xff81, dar variabila unsigned char 'x81'

Devine intregul 0x0081. Extensia de semn poate aparea daca tipul char este implicit signed( ceea ce se intampla la Turbo C).Exista o optiune de compilare care face ca tipul char sa fie implicit unsigned, dar este bine sa se scrie programme care sa nu depinda de acesta optiune.

Un alt fapt care trebuie avut in vedere este ca C-ul este un limbaj de tip case-sensitive, deci conteaza daca identificatorii sunt scrisi cu litere mici sau mari.Trebuie fortata la TASM optiunea /ml sau /mx care genereaza simbolurile tinand cont de acest fapt . Sa consideram urmatoarea secventa C :


Include <stdio.h>

Int n=5;

Char sir[]=" un sir " ;

Void main(void) 



Apelul lui printf din ASM se poate face cu secventa ASM de mai jos ( echivalenta cu secventa C):


.model small

extrn printf: near

.data

n dw 5

sir db 'un sir',0

_format db '%s %dn'

.code

_main proc near

push _n

lea ax, sir

push ax

lea ax ,format

push ax

call near ptr printf

add sp, 6

retn

_main endp


Sunt posibile doua moduri de dezvoltare a aplicatiei: in mediul integrat C sau prin linia de comanda.

In mediul integrat ( lansat prin comanda bc) trebuie definit un proiect care sa specifice explicit modulele in limbaj de asamblare.Definirea proiectului permite specificarea programului translator( compiler sau assembler).

Astfel se poate lucra in regim de editare, compilare , depanare etc, cu ambele tiprui de modele ( ASM si C), mediul integrat recunoscand modulele scrise in ASM.Acest context este foarte util pentru depanare, deoarece toate facilitatile specifice mediilor integrate de dezvoltare pentru limbaje de nivel inalt( rulare pas cu pas, vizualizarea permanenta a unor variabile etc,), se aplica si modulelor scrise in ASM.

O alta varianta de depanare o constitue Torbo debuggerul care poate fi apelat direct sau din mediul integrat, acestra recunoscand de asemenea atat module ASM cat si module C.

Optiunile necesare la compilarea programelor C( cea mai importanat fiind modelul de memorie) se stabilesc prin comanda options a mediului integrat.

A doua posibilitate este compilarea, respectiv asamblarea separata a modulelor suresa , prin lansari ale compilatorului bcc si ale asamblorului tasm de la consola si apoi legarea explicita a modulelor obiect , prin lansarea editoruluide legaturi tlink. In acest caz optiunea de model de memorie pentru modulele C se stabileste in linia de apel a compilatorului ( optiunea -mx, unde x este un caracter ce identifica modelul).De asemenea trebuie precizata optiunea -c ( compile only) astfel incat compilatorul C sa produca numai modul obiect.Tot in linia de comanda se precizeaza caile pentru fisierile header si pentru biblioteci( optiunile -I si -L).

Cand se lucreaza in regim de linie de comanda trebuie tinut seama de structura unui program C.Pe langa modulele definite de utilizator, trebuie adaugate modulul de initializare c0x( se pune pe prima pozitie in lista de module obiect la tlink) si bibliotecile C ( aflate in sobdirectorul lib), care se specifica dupa numele fisierului executabil.Atat modulul de initializare, cat si bibliotecile sunt specifice modelului de memorie folosit, fiind identificate prin ultima litera a numelui fisierului respectiv.

Bibliotecile care trebuie specificate depind de functiile utilizate in modulele C.Practic, se va lega todeauna biblioteca cx.lib si eventual , mathx.lib, emu.lib( pentru emularea operatiilor in virgula mobila), fp87.lib ( daca sistemul are coprocesor), graphics.lib(daca s-au folosit functii grafice). In specificarea numelor fisierelor care contin modulul de initializare si bibliotecile respective, caracterul x depinde de modelul de memorie folosit.

Este utila o vizualizare a continutului subdirectorului lib din implementare Boralnd C, penrtu a identifica bibliotecile C. Este de asemenea utila vizualizarea optiunilor posibile de compilare, asamblare si link-editare, ca si sintaxa acestor comenzi( se poate obtine prin bcc, tasm sau tlink urmate imediat de <enter>.

In cazul in care modulul de program principal este scris in limbaj de asamblare , trebuie tinut seam de faptul ca main este o functie ca oricare alta.De fapt modulul de program principal este modulul precompilat c0, care, dupa ce face o serie de initializari, apeleaza procedura main.Din aceasta cauza, daca functia main este scrisa in ASM, ea trebiue declarata ca o procedura si specificata ca simbol public:

Public main

_main proc

.

ret

_main endp

end

Procedura _main trebuie incheiata prin instructiunea ret, ca orice procedura, iar modulul de program respectiv nu trebuie sa contina eticheta de start.Incheierea executiei se va face la return-ul din _main, controlul revenind modulului c0, care a pelat _main si care este raspunzator de iesirea in sistemul de operare.Nu este necesara o iesire in DOS din procedura _main.De asemenea, nu este necesara nici initializarea registrelor DS si ES, deoarece este facuta in modulul c0.Modulul c0 defineste si o stiva minima(daca modelul nu este tiny), care se poate mari, dca este necesar, prin directiva ASM.stack .

In cazul in care functia cmain este scrisa in C, modulele ASM vor contine practic proceduri si definitii de date care trebuie declarate publice(cu ajutorul directivei ASM public). De asemenea daca modulele ASM apeleaza functii C acestea trevuie declarate in ASM ca simboluri externe(cu ajutorul directivei ASM extern)

Prezentam in continuare, sursa in asamblare a unui program care permite tiparirea unui numar de cuvinte la intrarea standard, si dupa ce a fost tiparit CTRL-Z (marker de sfirsit de fisier) apoi ENTER raporteaza numarul de cuvinte tiparite. Cuvintele sunt siruri de caractere separate prin spatii, caractere TAB, Carriage Return sau Line Feed. Programul este important in masura in care va permite familiarizarea cu conceptul de stiva, cu operatiile care pot fi efectuate pe stiva,cu modul in care sunt definite si apelate subrutinele in asamblare.

De asemenea, el va pune la dispozitie doua rutine, anume Convert Number To String si Print String pe care le veti utiliza foarte des in programele dv. viitoare.

Inainte de a trece la o scurta descriere logica a programului,sa ne concentram putin atentia asupra conceptului de stiva.


Stiva. Operatii cu stiva


Stiva este o structura LIFO (Last In First Out), organizata intr-o zona a memoriei, in forma unui segment, declarata ca atare prin program.

Elementul asupra caruia se opereaza este intotdeauna cel referit de 'top-ul' stivei (deci refera primul element) care este cel mai recent introdus si primul extras. Operatiile care pot fi efectuate asupra stivei sunt:- 'push' care adauga un element nou in 'capul' stivei si incrementeaza top-ul, 'pop' care extrage primul element din stiva si decrementeaza top-ul.

Registrul segment dedicat pentru lucru cu stiva este SS (el continind

adresa de inceput a segmentului), iar registrul folosit pentru adresarea elementelor in segmentul de stiva este SP. La calculatoarele din familia 80x86, stiva 'creste' de la adrese mari la adrese mici; SP contine deplasarea ultimului cuvint intrat in stiva. Cuvintele sunt plasate in stiva la adrese de memorie consecutive descrescator. De aceea, operatiile in lucrul cu stiva (reflectate in istructiunile PUSH si POP) vor afecta continutul registrului SP exact invers decit cum am edscris mai sus: PUSH decrementeaza SP, iar POP incrementeaza SP.

Pentru a simti cum anume este folosit SP in lucrul cu stiva, vom descrie in continuare instructiunile PUSH si POP.



Instructiunea PUSH: Pune un cuvint in stiva


Folosire:

PUSH sursa


Decrementeaza continutul registrului SP cu doi si apoi transfera un cuvint din operandul sursa in noul virf al stivei, referit de SP (deci care are adresa SS:SP. Aceasta instructiune este folosita deseori pentru incarcarea parametrilor in stiva, inainte de apelul unei proceduri.


Instructiunea POP: Extrage un cuvint din stiva


Folosire:

POP destinatie


Transfera cuvintul din top-ul stivei (SS:SP) la locatia referita de 'destinatie', apoi incrementeaza SP cu 2 astfel incit sa pointeze la noul virf al stivei.


Definirea si apelul procedurilor


In cele ce urmeaza, vom descrie cum anume sunt definite si apelate procedurile in limbaj de asamblare.

Procedurile sunt sectiuni de cod apelate in executie din diferite locuri ale programului. La fiecare apel al procedurii, sunt executate instructiunile care compun procedura, dupa care controlul programului este predat instructiunii de dupa apelul procedurii.

Orice definire a unei proceduri (adica descrierea instructi-unilor care trebuiesc executate la apelul procedurii) sunt delimitate de cuvintele rezervate PROC si ENDP (End Procedure).


nume PROC [NEAR|FAR]


; instructiuni componente ale procedurii


nume ENDP


Codul unei proceduri se poate afla in alt segment de cod decit cel in care rezida apelul ei. De aceea, vom manevra termenii de procedura NEAR si procedura FAR. In cazul in care lucram intr-un acelasi segment de cod (numim aceasta 'intrasegment') proce-durile sunt NEAR, apelurile sunt CALL intrasegment iar predarea controlului dupa terminarea procedurii se face printr-un RET intrasegment. Daca procedura rezida intr-un alt segment de cod decit cel in care se face apelul, procedura este FAR (numim acesta

'intersegment') si, similar, avem CALL intersegment si RET intersegment.

Deja am observat, tipul unei proceduri se specifica la definirea ei folosind cuvintele rezervate NEAR sau FAR. Daca nu se specifica, implicit tipul procedurii este NEAR. Aceasta specificare ajuta asamblorul sa decida la asamblarea instructiuni-lor de apel si de return ce fel de CALL-RET (intra sau inter-segment) sa genereze. Diferenta intre instructiunile CALL-RET intrasegment si CALL-RET intersegment este, in linii mari, urmatoarea:


- intrasegment: la executia unui CALL, este plasat (mai corect, salvat) in stiva continutul registrului IP, iar IP este incarcat cu adresa relativa la segmentul de cod a locatiei in care incep instructiunile componente ale procedurii; la executia unui RET,    registrul IP este incarcat cu ceea ce a fost salvat anterior la un CALL in stiva (printr-o operatie 'pop') astfel incit programul sa poata relua executia din punctul de dupa apelul procedurii.


- intersegment: pe linga salvarea (respectiv restaurarea) registrului IP, are loc operatia similara si pentru registrul CS (natural,atita timp cit segmentul de cod difera, este nevoie sa tin minte adresa segmentului din care s-a facut apelul, pentru continuarea executiei programului).


Instructiunea CALL

Folosire:

CALL nume_procedura


Instructiunea RET

Folosire:

RET numar_octeti


Daca numar_octeti este specificat, SP este modificat conform operatiei SP:=SP+numar_octeti, dupa care este restaurat CS (doar daca procedura este FAR) si apoi IP.


Programul nostru foloseste doar proceduri NEAR.


Program care numara cuvintele dintr-un fisier. Cuvintele sunt delimitate de separatori: spatii, caractere TAB, CR sau LF.


stack segment stack

DW 1000 dup(?) ; rezerv 1000 de cuvinte pentru stiva

stack_top label word ; stiva 'creste' de la adrese mari la

;adrese mici

stack ends


my_data segment

Count DW 0 ; variabila folosita pentru a numara cuvintele

InSeparator DB ? ; variabila setata pe 1 cind ultimul caracter citit

;a fost separator

TempChar DB ? ; folosita temporar de procedura GetNextCharacter

Result DB 'Word count 5 DUP (?)

; mesaj care va fi tiparit la raportarea numarului

;de cuvinte tiparit

CountInsertEnd LABEL BYTE ; folosit pentru a gasi sfirsitul zonei in care

;rezida sirul ce tine valoarea numarata

DB 0dh, 0ah,'$'

; apelul sistem 09h asteapta ca sirul de caractere

;ce urmeaza a fi afisat sa se termine cu $

my_data ends



my_code segment

assume cs:my_code, ds:my_data, ss:stack

ProgramStart:

mov ax,my_data

mov ds,ax ; DS va pointa la segmentul my_data

mov ax, stack

mov ss, ax ; nu mut niciodata valoare imediata in registru

;segment

mov sp, OFFSET stack_top ; registrul SP va contine acum top-ul stivei

mov [InSeparator],1 ; initializez InSeparator cu 1, in ideea ca

;primul caracter diferit de separator va marca

;inceputul unui cuvint


CountLoop:

call GetNextCharacter ; apelez procedura care citeste urmatorul caracter

jz CountDone ; daca a fost citit caracterul care marcheaza

;sfirsit de fisier (^Z), sari la CountDone

call IsSeparator ; este caracterul curent separator?

jz ItIsSeparator ; daca da, sari la ItIsSeparator

cmp [InSeparator],0 ; altfel, verific daca InSeparator nu este setat

;curent pe 0

jz CountLoop ; daca este 0 (iar caracterul n-a fost separator),

;inseamna ca am terminat cu acest caracter, deci

;putem relua bucla

inc [Count] ; altfel, caracterul curent nu este separator,

;deci am gasit inceputul unui cuvint nou

mov [InSeparator],0 ; marchez cum ca nu mai sunt curent pe separator

jmp CountLoop ; reiau procesul pentru caracterul urmator

ItIsSeparator:

mov [InSeparator],1 ; marchez cum ca sunt curent pe separator

jmp CountLoop ; reiau procesul pentru caracterul urmator


; De la eticheta CountDone incep instructiunile care raporteaza rezultatul


CountDone:

mov ax,[Count] ; trebuie sa convertesc Count la sir de caractere

;pentru a-l putea afisa

mov bx,OFFSET CountInsertEnd-1

; BX va pointa la sfirsitul sirului la care voi

;apenda numarul

mov cx,5 ; numarul de cifre de convertit

call ConvertNumberToString

; apelez procedura care face conversia

mov bx,OFFSET Result ; BX pointeaza la sirul rezultat

call PrintString ; apelez procedura care afiseaza rezultatul

mov ah,4ch ; chem functia sistem care termina executia

int 21h ;programului


; Procedura GetNextCharacter citeste urmatorul caracter de la intrarea

; standard.


; Intrare: Nici una


; Iesire:

; AL = caracterul, daca a fost tiparit unul

; indicatorul ZF flag = 0 daca a fost tiparit un caracter,

= 1 daca a fost detectat sfirsit de fisier


; Registrii distrusi: AH, BX, CX, DX


GetNextCharacter PROC

mov ah,3fh ; apel sistem care citeste dintr-un fisier

mov bx,0 ; incarc AX cu identificatorul logic al intrarii

;standard

mov cx,1 ; numarul de caractere pe care vreau sa-l citesc

;este 1

mov dx,OFFSET TempChar ; vreau sa pun caracterul in TempChar

int 21h

jc NoCharacterRead ; daca CF a fost setat, deci este eroare,

;trateaza ca si cum ar fi fost citit sfirsit de

;fisier (dci n-am citit nici un caracter)

cmp [TempChar],1ah ; a fost Control-Z (sfirsit de fisier)?

jne NotControlZ ; daca nu, (Jump if Not Equal) sari la NotControlZ

NoCharacterRead:

xor ax,ax ; setez numarul de caractere citite (AX:=0)

;(n-am citit nici un caracter, dar aceasta

;ar putea fi ca urmare fie a faptului ca am

;introdus CTRL-Z, fie ca a aparut o eroare. In

;ultimul caz, AX contine codul erorii.

NotControlZ:

and ax,ax ; setez ZF pe 0 ca sa reflecte daca a fost citit

;un caracter, ori pe 1 daca am gasit sfirsit de

;fisier.

mov al,[TempChar] ; returnez caracterul citit

ret ; GATA!

GetNextCharacter ENDP


; Procedura IsSeparator raporteaza daca un caracter dat este separator


; Intrare:

; AL = caracterul de verificat


; Iesire:

; Z flag = 0 daca octetul citit nu este separator

; = 1 daca octetul citit este separator


; Registrii distrusi: nici unul


IsSeparator   PROC

cmp al,' ' ; este spatiu?

jz EndIsSeparator ; daca da, este separator

cmp al,09h ; este tab?

jz EndIsSeparator ; daca da, este separator

cmp al,0dh ; este Carriage Return?

jz EndIsSeparator ; daca da, este separator

cmp al,0ah ; este Line Feed? Daca da, este separator,

;deci returneaza 1

; daca nu, nu este separator, si atunci returneaza

asa cum a fost setat de cmp

EndIsSeparator:

ret

IsSeparator   ENDP



; Procedura ConvertNumberToString converteste un numar binar la un sir de

;caractere


; Intrare:

; AX = numarul de convertit

; DS:BX = adresa la sfirsitul sirului in care textul va fi stocat

; CX = numarul de cifre de convertit


; Iesire: Nimic


; Registrii distrusi: AX, BX, CX, DX, SI


ConvertNumberToString PROC

mov si,10 ; folosit pentru impartire la 10

ConvertLoop:

xor dx,dx

div si ; impart numarul la 10. Restul este in DX iar citul in AX

add dl,'0' ; convertesc restul la caracter

mov [bx],dl ; pun in sir

dec bx ; merg la locatia unde se afla urmatoarea cifra

loop ConvertLoop ; procesez urmatoarea cifra, daca mai este vreuna

ret

ConvertNumberToString ENDP


; Procedura PrintString tipareste un sir de caractere pe ecran


; Intrare:

; DS:BX = adresa zonei unde este stocat sirul de tiparit


; Iesire: Nimic


; Registrii distrusi: Nici unul


PrintString PROC

push ax

push dx ; salvez in stiva registrii AX, DX

mov ah,9 ; apel sistem pentru afisarea unui sir de caractere

mov dx,bx

int 21h

pop dx ; restaurez registrii pe care i-am modificat

pop ax

ret

PrintString ENDP


my_code ends

END ProgramStart

;AAAAAAA AAAAAAAA AAAAAAAA AAAAAAAAAAAAAA AAAAAAAAAAAA AAAAAAA AAAAAAA AAAAAAAAA


Variabilele si etichetele au caracteristici care le disting numite attribute care raspund la intrebarile:

& unde este variabila/eticheta definita?

De fapt aceasta intrebare poate fi divizata in:

1)In care segment ( iata atributul SEGMENT) variabila/eticheta e definita? si

2)Ce deplasament (iata atributul OFFSET) are variabila/eticheta fata de inceputul segmentului?

& In ce mod se intentioneaza a se utiliza variabila/eticheta?

Pentru o variabila de interes ar fi lungimea declarata, aceasta constituind atributul TYPE (byte, word,dword,). In ceea ce priveste o eticheta, ea ar putea preceda o secventa de instructiuni la care se doreste sa se faca un salt si astfel ar putea fi referita spre exemplu din segmentul curent sau dintr-un alt segment ('de la distanata').

Deci pt o eticheta este necesar un atribut numit DISTANCE. Pt. o eticheta acest atribut ar putea avea valoarea NEAR (deci ea e utilizata in segmentul curent) sau FAR, indicindu-se posibi-litatea referirii dintr-un alt segment.

Totusi exista si etichete care preceda date, acestea urmind sa aiba un atribut TYPE (byte, word,) asa cum vom vedea mai jos. Dar sa ne indreptam atentia (dind un mic exemplu) asupra etichetelor ce preceda cod (instructiuni).

Daca o eticheta este referita numai din segmentul in care a fost definita atunci ea poate fi declarate cu atributul DISTANCE egal cu NEAR.

O eticheta poate fi implicit declaraa prin simpla prezenta urmata de ':'in fata unei linii de cod; o astfel de eticheta este intotdeauna NEAR.

Daca o instructiune Call sau Jmp din alt segment refera o eticheta, atributul sau DISTANCE trebuie declarat FAR.De exemplu : et1 LABEL FAR ; se poate face JMP/CALL aici din alte segmente

et2: mov dx,bx ; JMP/CALL aici numai din segmentul curent. Aici directiva LABEL pe care o vom detalia in continuare declara et1 a fi o eticheta FAR care poate fi referita de un cod dintr-un alt segment.

Deasemenea et2 este o eticheta deoarece preceda ':' dar ea este o eticheta NEAR si poate fi referita doar din acest segment.

S-a vazut din cele date mai sus ca o eticheta poate precede o secventa de cod, unde este posibil a se face un salt prin referirea etichetei intr-o instructiune de salt (jump) sau apelare (call). De fapt o eticheta poate identifica nu numai O SECVENTA DE INSTRUCTIUNI DAR deasemenea SI DATE. Vom vedea acest lucru urmarind forma generala a unei directive LABEL prin care se declara o eticheta:


name LABEL type


unde name este numele etichetei careia ii vor fi asociate urmatoarele atribute:

1)SEGMENT- segmentul curent care se asambleaza (in care apare directiva)

2)OFFSET- deplasamentul in cadrul segmentului

3)al treile atribut este descris de fapt de 'type' care este prezent in directiva. 'type' poate fi:

a) NEAR sau FAR (deci este vorba de un atribut DISTANCE) propriu etichetelor care preceda cod executabil. In acest caz eticheta poate fi utilizara in salturi sau apelari dar nu in instructiuni MOV sau alte instructiuni ce manipuleaza date.

b) BYTE, WORD, DWORD sau altele (cum ar fi nume de structura, nume de inregistrare) propriu etichetelor care preced date. In acest caz eticheta poate fi privita ca desemnind date si poate fi utilizata in instructiuni MOV, ADD, etc., dar nu direct in salturi sau apelari(jump, call); aici ar fi vorba de un atribut TYPE, ca pentru variabile.


Principalele utilizari ale unei directive LABEL ar fi:

1. Sa acceseze variabile ce au un anumit tip prin (de eemplu) BYTE sau WORD, sau dupa cum se doreste. De exemplu:

et LABEL BYTE

vect DW 1000 DUP(0)

Astfel putem scrie instructiunile:

- add AL, et[99]; aduna al 100-lea byte din vect la registrul AL

- add AX, vect[0] ; aduna primul cuvint din vect la registrul AX


2. Sa se defineasca o eticheta FAR (asa cum s-a vazut intr-un exemplu precedent).


3. Sa permita accesarea unei secvente de cod precedata de o eticheta urmata de ':' (care nu poate fi decit NEAR, deci poate fi referita doar din segmentul curent) dintr-un alt segment. Asta se poate face prin utilizarea unei directive LABEL FAR care va preceda eticheta mentionata ca in exemplul urmator:


et_dist LABEL FAR

et: add AX,BX

. cod ..

Eticheta et poate fi referita doar din segmentul curent deci secventa de cod care ii urmeaza nu poate fi 'apelata' din alt segment prin intermediul referirii acesteia (fiind o eticheta NEAR) dar in schimb acest lucru se poate face referind eticheta et_dist care esta FAR.


In cele ce urmeaza, vom descrie instructiunile nou introduse

in acest program.


Instructiunea JNE/JNZ: Jump Not Equal / Jump Not Zero


Folosire:

JNE eticheta

JNZ eticheta


Daca indicatorul ZF este 0, cauzeaza continuarea executiei

programului de la instructiunea precedata de 'eticheta:'.


Instructiunea JZ/JE: Jump Equal / Jump Zero


Folosire:

JE eticheta

JZ eticheta


Daca indicatorul ZF este 1, cauzeaza continuarea executiei

programului de la instructiunea precedata de 'eticheta:'.


Instructiunea DIV: Divide


Folosire:

DIV sursa


Impartire fara semn a acumulatorului (AX) cu sursa. Daca operandul sursa este un octet, citul este plasat in AL, iar restul in AH. Daca operandul sursa este un cuvint, citul este in AX, iar restul in DX. Daca restul depaseste capacitatea registrului destinatie (FFh pentru sursa octet ori FFFFh pentru sursa cuvint) sau daca a fost incercata o impartire cu 0,este generata o intrerupere de tip 0 (impartire la 0), restul si citul avind valori nedefinite.



Instructiunea LOOP: Executa o secventa de instructiuni de cite ori indica CX (bucla)


Folosire:

LOOP eticheta


Instructiunea LOOP decremeteaza continutul registrului CX si, dacaCX in urma decrementarii e diferit de 0, transfera controlul programului la instructiunea precedata de 'eticheta'. Operandul 'eticheta' trebuie sa denote o adresa in domeniul -128 127 relativ la instructiunea care precede LOOP.


Programul foloseste un alt apel sistem, anume 3Fh, a carui descriere o dam in cele ce urmeaza.

Functia 3Fh - citeste dintr-un fisier sau dispozitiv, folosind indicator logic


Inainte de apel trebuiesc incarcati:

AH = 3Fh

BX = numarul de indicator logic

CX = numarul de octeti de citit (caractere citite)

DS:DX = adresa zonei in care sunt scrise caracterele citite



Dupa apel seteaza:

CF = 1 daca a fost eroare


AX = numarul de octeti cititi, daca CF nu a fost setat pe 1 sau cod de eroare, daca CF a fost setat.


Comentarii:

- functia citeste numarul de octeti specificati, aducindu-i in zona adresata de DS:DX.

- daca AX nu este egal cu CX, atunci a aparut o citire partiala

deoarece a fost intilnit sfirsit de fisier.

- daca AX este 0, nici o data nu a fost citita, deci sfirsitul de fisier a aparut inainte de citire.

- intrarea standard are identificator logic cu numarul 0.

De asemenea, programul foloseste codurile ASCII: 1Ah - sfirsit de fisier, 09h - tab, 0Ah, 0Dh.


Dezvoltare intr-un limbaj mixt(C si ASM)


Mediul de dezvoltare Borland C ofera posibilitatea inserarii de cod ASM in programe sursa C.Fiecare instructiune ASM trebuie pecedata de cuvantul cheie asm sau sa fie inclusa intr-un bloc de tip ASM ,de forma :


asm

Dorim acum sa scriem aceeasi functie in 'limbajul CAS'.Presupun pentru inceput un model 'de date mici'.Implementarea functiei este urmatoarea :


int cauta (int near*a,size_t n,int x)


reluare

asm

not_gasit

asm mov ax, -1 */nu s-a gasit*/

*/elementul x*/

final

asm

return _AX */instructiune*/


Se observa tehnica de scriere a buclei de cautare (prefixul LOOPNZ)si calculul indicelui elementului gasit, ca diferenta dintre dimensiunea n a tabloului si valoarea incrementata a contorului CX la iesirea din bucla.Revenirea in functia apelanta se face cu instructiunea C return_AX.

Un punct important este incarcarea adresei tabloului.Daca adresa este near, o incarcam cu o instructiune MOV (in stiva se gaseste deplasamentul in cadrul segmentului curent de date ).In cazul unui model de 'date mari', in stiva se gaseste un pointer far(o variabila de tip DoubleWord),caz in care incarcarea s-ar face cu o instructiune :


les si, a */adresa far a tabloului*/


Compararea elementului curent cu cel cautat s-ar scrie :


cmp es  si], dx */comparam cu cel cautat*/


In fine , ar mai trebuit salvat si restaurat registrul ES , iar prototipul functiei ar trebui scris sub forma :


int cauta (int far*a,size_t n,int x) ;


Modificarile de mai sus transforma functia intr-una adecvata modelelor'de date mari'.

Un program de test pentru functia de mai sus s-ar scrie in genul :


#include<stdio.h>

int x[]= ;

# define NREL(a) (sizeof(a)/sizeof(a[0]))

void main (void)



Trebuie totusi observat ca aceasta tehnica mixta de dezvoltare are si dezavantaje.Un modul CAS este destul de greu de intretinut, iar transportul sau in alt mediu de dezvoltare decat Borland ar putea crea probleme de compatibilitate.Tehnica mixta merita folosita atunci cand avem de implementat secvente relativ scurte de program,pentru care nu se justifica un modul(fisier sursa)separat.


Performante in ASM si C


In acest subcapitol ,vom face o analiza comparativa a unui algoritm implementat in C si ASM.Algoritmul ales este un algoritm de sortare interna , anume sortarea prin partitionare si interschimbare, numita si sortare rapida(quicksort).

Algoritmul este recursiv si se poate implementa foarte comod atat in C, cat si in ASM.O implementare polimorfica a acestui algoritm este oferita si de biblioteca standard a limbajului C.


A.Implementarea in C


Varianta C considerata va fi de asemenea polimorfica (capabila sa sorteze tablouri de orice fel).Prototipul functiei C este :


Void qsort (void*tab,size_t n,size_t size,PFCMP cmp) ;


Semnificatia parametrelor este :

Tab-adresa de inceput a tabloului ;

N-numarul de elemente ale tabloului ;

Size-dimensiunea in octeti a unui element ;

Cmp-pointer la o functie externa de comparatie ,definit prin :

typedef int (*PFCMP) (void*a,void*b) ;


Functia de comparatie(scrisa de utilizator)primeste adresele a doua elemente din tablou si intoarce o valoare negativa, zero sau pozitiva ,dupa cum elementul de la prima adresa este « mai mare », « egal », sau « mai mic » decat elementul de la a doua adresa.Sensul notiunilor mai mare, egal sau mai mic este complet abstract , fiind definit de utilizator chiar prin functia de comparatie.

Pentru transferul datelor , se vor utiliza functiile ajutatoare swap si copy, listate mai jos.


typedef unsigned char BYTE ;

void swap(void*a,size_t size,int i, int j)



void copy(void*a,void*b,size_t size)


Algoritmul de sortare rapida este recursiv.Functia care implementeaza algoritmul recursiv primeste ca date de intrare o parte a tabloului care trebuie sortat, prin adresa de inceput si doi indici notati left si right.Initial, functia se apeleaza cu indicii 0 si n-1.

Se alege un element arbitrar al tabloului v, numit pivot, notandu-l cu mark(variante uzuale sunt v[left] sau v[(left+right)/2]).Se partitioneaza tabloul in raport cu mark , in sensul ca toate elementele aflate la stanga lui mark sa fie mai mici sau egale cu aceasta, iar toate elementele aflate la dreapta lui mark sa fie mai mari sau egale cu aceasta.

In acest moment ,pivotul se afla pe pozitia sa finala, iar tabloul este partitionat in 2 subtablouri.Se apeleaza acum aceeasi functie , cu indicii left si k-1,respectiv k+1 si right,unde k este indicele pivotului in urma partitionarii.In felul acesta ,se sorteaza cele 2 subtablouri.Daca left>=right, algoritmul se opreste.

Algoritmul se poate imbunatati in felul urmator.Sa pornim cu doi indici i si j, initializati cu left si, respectiv ,cu right.Cat timp v[i]<mark, incrementam i,apoi,cat timp v[j]>mark,decrementam j.Daca acum i<=j interschimbam v[i] cu v[j] actualizand similar indicii i si j.Tot acest proces continua pana cand i>j.

Acum se apeleaza recursiv functia , cu indicii left si j,respectiv i si right(daca left <j,respectiv daca i<right).

In implementarea polimorfica ,deoarece la compilare nu se cunoaste dimensiunea unei inregistrari ,se aloca spatiu dinamic pentru inregistrarea mark,copiindu-se elementul din mijlocul tabloului si eliberand spatiul la iesirea din functie.

O imbunatatire posibila a metodei este recurgerea la o metoda directa de sortare ,daca lungimea tabloului este inferioara unei limite prestabilite, evitand astfel apelul recursiv.De exemplu la un tablou de de 1000 de inregistrari, cand dimensiunea partitiei ajunge 2,se vor face 500 de apeluri ale functiei, pentru a sorta de fiecare data un tablou de 2 elemente.Este mult mai eficient daca acest lucru se face direct.In implementarea de fata, s-a ales limita 2, caz in care cele doua inregistrari se compara direct.Pentru eficienta ,variabilele intens folosite in etapa de partitionare (i si j) sunt declarate in clasa register.



Implementarea este urmatoarea :


void quick_sort(BYTE*v,size_t size, int left,int right,PFCMP cmp)


mark=(BYTE*)malloc(size) ;

copy(mark,v+((left+right)/2)*size,size) ;

dowhile(i<=j) ;

if(left<j)

quick_sort(v,size,left,j,cmp) ;

if(i<right)

quick_sort(v,size,i,right,cmp) ;

free(mark) ;


void Quick_C(void*v,size_t n,size_t size,PFCMP cmp) ;


Implementarea in ASM   



Sa consideram acum implementarea algoritmului de sortare rapida in limbaj de asamblare intr-o versiune specializata(pentru tablouri de intregi).Toate adresele se considera de tip near,fiind offset-uri in cadrul segmentului adresat prin registrul DS.

Algoritmul este acelasi cu cel pezentat mai sus,implementarea ASM urmarind exact programul C.Variabilele din descrierea in C a algoritmului sunt mentinute in registrele procesorului si in stiva.

Parametrii se transmit prin stiva,conform sablonului definit in program,iar stiva este descarcata de programul apelant.Aceasta madalitate de apel permte ca functia ASM sa poata fi apelata din C,daca modelul cu care se lucreaza este small.



.model small

.code

Public _qasm


; qasm(int v[],int left,int right)


Sablon struc

_bp_ip dw 2 dup( ?)

V dw ?

Left dw ?

Right dw ?

Sablon ends

_qasm proc near

Push bp

Mov bp,sp

Push a


;Asignarea variabilelor


;i=si,j=di,v=bx,mark=dx

;left,right=in stiva


Mov bx, [bp].v ;v

Mov si, [bp].left ;i=left

Mov di, [bp].right ;j=right

Mov ax, di ;compara

Sub ax, si ;right-left

Cmp ax, 1 ;cu 1

Jng et00

Jmp et1 ;mai mare :se executa

;rutina normal

Et00 :

Je et0 ;right=left+1

Jmp gata ;left=right :iesire

Et0 :

;

;sunt 2 elemente

;se compara si eventual se schimba

;

Sh1 si, 1 ;intregii sunt pe

Sh1 di, 1 ;doi octeti

Mov ax, [bx si] ;v[left]

Cmp ax, [bx di] ;v[right]

Jnle aici

Jmp gata ;sunt o.k.

Aici :

Xchg ax, [bx di] ;daca nu,se

Mov [bx si], ax ;schimba

Jmp gata ;si gata

Et1 :


;sunt mai mult de doua elemente


Mov ax, si ;calcul

Add ax, di ;(left+right)/2

Shr ax, 1

Push bx

Shl ax, 1 ;adresa se aduna

Add bx, ax ;la v

Mov dx, [bx] ;mark=v[(left+right)/2]

Pop bx

_do :  ;ciclul do

While_i ;ciclul while dupa

;v[i]<mark

Sh1 si, 1 ;compara v[i] cu

Cmp [bx si], dx ;mark

Jde end_i ;sfarsit ciclu daca

;v[i]>=mark

Shr si, 1

Inc si ;daca nu se face i++

Jmp while_i ;si se reia

End_i :

Shr si, 1

While_j ;ciclul while dupa

;v[j]>mark

Sh1 di, 1

Cmp [bx di], dx ;compara v[j] cu mark

Jle end_j ;sfarsit ciclu daca

;v[j]<=mark

Shr di, 1

Dec di ;daca nu,se face j-

Jmp while_j ;si se reia

End_j :

Shr di, 1


Cmp si, di ;compara i cu j

Jg _while ;salt daca i>j


Sh1 di, 1 ;intregii sunt

Sh1 si, 1 ;pe doi octeti

Mov ax, [bx si] ;schimba v[i]

Xchg ax, [bx di] ;cu

Mov [bx si], ax ;v[j]

Gata :

Popa

Pop bp

Ret

_qasm endp

End



;interfata cu limbajul C se realizeaza prin functia quick_a

;care are un prototip asemanator cu functia quick_c.


;void quick_a(void*v,size_t n,size_t size ,PFCMP cmp) ;


Compararea performantelor


Evaluarea performantelor unui algoritm de sortare interna consta in estimarea sau in masurarea numarului de operatii de comparatie intre elemente si a numarului de operatii de copiere sau interschimbare a doua elemente.Aceste evaluari se efectueaza in trei situatii distincte de tablouri :tablou aleator,tablou deja sortat(cazul cel mai favorabil) si tablou sortat invers(cazul cel mai defavorabil).


Nu se poate descarca referatul
Acest document nu se poate descarca

E posibil sa te intereseze alte documente despre:


Copyright © 2024 - Toate drepturile rezervate QReferat.com Folositi documentele afisate ca sursa de inspiratie. Va recomandam sa nu copiati textul, ci sa compuneti propriul document pe baza informatiilor de pe site.
{ Home } { Contact } { Termeni si conditii }