Administratie | Alimentatie | Arta cultura | Asistenta sociala | Astronomie |
Biologie | Chimie | Comunicare | Constructii | Cosmetica |
Desen | Diverse | Drept | Economie | Engleza |
Filozofie | Fizica | Franceza | Geografie | Germana |
Informatica | Istorie | Latina | Management | Marketing |
Matematica | Mecanica | Medicina | Pedagogie | Psihologie |
Romana | Stiinte politice | Transporturi | Turism |
Universitatea "Al. I.
Cuza"
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).
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 } |
Documente similare:
|
ComentariiCaracterizari
|
Cauta document |