CATEGORII DOCUMENTE |
Intreruperi
Notiunea de intrerupere presupune (asa cum ii arata si numele) intreruperea programului in curs de executie si transferul controlului la o anumita rutina specifica, numita rutina de tratare, dictata de cauza care a generat intreruperea. Mecanismul prin care se face acest transfer este, in esenta, de tip apel de procedura, ceea ce inseamna revenirea in programul intrerupt, dupa terminarea rutinei de tratare.
1 Intreruperi hardware si software. Tabela de intreruperi
Intreruperile se pot clasifica dupa mai multe criterii, rezultand tipurile ilustrate in
Figura 2.7.
Figura 2.7 Clasificarea intreruperilor
Intreruperile software apar ca urmare a executiei unor instructiuni, cum ar fi INT, INTO, DIV, IDIV;
Intreruperile hardware externe sunt provocate de semnale electrice care se aplica pe intrarile de intreruperi INT si NMI ale procesorului;
Intreruperile hardware interne apar ca urmare a unor conditii speciale de functionare a procesorului (cum ar fi executia pas cu pas a programelor);
Intreruperile dezactivabile sunt provocate de semnalul electric aplicat pe linia (intrarea) INT si sunt luate in considerare numai daca bistabilul IF este 1.
Intreruperile nedezactivabile sunt provocate de semnalul electric aplicat pe linia NMI si sunt totdeauna luate in considerare.
Intreruperile hardware dezactivabile sunt controlate de unul sau mai multe circuite specializate (controlere de intreruperi 8259A), care accepta fiecare cel mult opt cereri de intreruperi, pe care le transmit catre linia INT a procesorului. Daca bistabilul IF este 1, procesorul raspunde printr-o secventa de semnale electrice INTA (Interrupt Acknowledge), asemanatoare unei secvente de citire a codului unei instructiuni. Pe durata unuia din aceste semnale, controlerul care a initiat cererea de intrerupere plaseaza pe magistrala de date octetul care identifica nivelul intreruperii. Un asemenea sistem se numeste sistem vectorizat de intreruperi (vezi Figura 2.8).
Figura 2.8 Intreruperi hardware vectorizate
Intr-un sistem cu procesor 8086, pot exista maxim 256 de intreruperi (nivele) distincte. Fiecare din aceste nivele poate avea asociata o procedura de tip far, numita rutina de tratare. Adresele acestor rutine sunt trecute intr-o asa numita tabela de intreruperi, aflata la adresele fizice 00000 - 003FFH, ocupand deci 1024 de octeti. Fiecare nivel ocupa 4 octeti, primii reprezentand offset-ul, iar urmatorii adresa de segment a procedurii (vezi Figura 2.9).
La aparitia unei intreruperi, au loc urmatoarele actiuni:
. se salveaza in stiva registrele FLAGS, CS si IP (in aceasta ordine);
. se sterg bistabilii IF si TF;
. se furnizeaza procesorului un intreg pe 8 biti (deci in gama 0 - 255), numit si vector de intrerupere, care identifica nivelul asociat intreruperii;
. se executa un salt indirect intersegment la adresa de inceput a rutinei de
tratare, prin intermediul tabelei de intreruperi.
Vectorul de intrerupere poate fi furnizat procesorului in urmatoarele moduri:
. in cazul intreruperilor hardware interne, nivelul este implicit; de exemplu, pentru intreruperea de executie pas cu pas (generata daca bistabilul TF=1). se utilizeaza totdeauna nivelul 1;
. in cazul intreruperilor hardware externe, nivelul este plasat pe magistrala de date, in cadrul ciclului masina (Interrupt Acknowledge), care urmeaza dupa luarea in considerare a intreruperii de catre dispozitivul care a generat intreruperea;
. in cazul intreruperilor software, nivelul este continut in instructiune.
Figura 2.9 Tabela de intreruperi
Se observa ca, exceptand salvarea flagurilor si stergerea bistabililor IF si TF, secventa de tratare a unei intreruperi se reduce la un apel indirect de procedura far, prin intermediul tabelei de intreruperi.
2 Instructiuni specifice intreruperilor (INT, IRET, INTO)
Instructiunea INT (Interrupt - Intrerupere software)
Are forma generala:
unde n este o constanta intreaga in gama 0 - 255.
Semnificatia este urmatoarea:
(SP) <- (SP) - 2
SS:((SP) + 1 : (SP)) <- (FLAGS)
IF <- 0, TF <- 0
(SP) <- (SP) - 2
SS:((SP) + 1 : (SP)) <- (CS)
(CS) <- (4*n + 2)
(SP) <- (SP) - 2
SS:((SP) + 1 : (SP)) <- (IP)
(IP) <- (4*n)
Se incarca, deci, in CS si IP continutul de la adresele fizice 4*n+2 si 4*n, adica cei patru octeti corespunzatori nivelului n din tabela de intreruperi.
Instructiunea IRET (Interrupt Return - Revenire din intrerupere)
Datorita actiunilor care au loc la aparitia unei intreruperi, o procedura de tratare nu se poate incheia cu o instructiune RETF, deoarece trebuie refacut si registrul de flaguri. Ca atare, procedurile de tratare a intreruperilor se incheie totdeauna cu instructiunea IRET.
Instructiunea, care este fara operanzi, are semnificatia urmatoare:
(IP) <- SS:((SP) + 1 : (SP))
(SP) <- (SP) + 2
(CS) <- SS: ((SP) + 1 : (SP))
(SP) <- (SP) + 2
(FLAGS) <- SS:((SP) +1 : (SP))
(SP) <- (SP) + 2
Se observa ca bistabilii IF si TF, care au fost stersi la aparitia intreruperii, sunt refacuti asa cum erau inainte de intrerupere, o data cu ceilalti bistabili din registrul FLAGS.
Instructiunea INTO (Interrupt if Overflow - Intrerupere in caz de depasire)
Instructiunea care este fara parametri, are semnificatia urmatoare:
. daca OF = 1, atunci se executa secventa corespunzatoare unei instructiuni
INT 4.
Codificarea instructiunilor INT este pe doi octeti, cu exceptia instructiunii INT 3, la care codificarea este pe un singur octet. Acest fapt nu este intamplator, deoarece nivelele 2 si 3 sunt, in general, utilizate de programele de depanare (debuggere).
Astfel, rularea pas cu pas a unui program se face setand bistabilul TF si scriind pe nivelul 1 al tabelei de intreruperi adresa unei rutine proprii de tratare. La executia fiecarei instructiuni, se va da controlul rutinei proprii, care va afisa corespunzator starea programului. Rularea pana la o adresa data (breakpoint) se realizeaza prin inlocuirea octetului de la adresa respectiva cu codul unei instructiuni INT 3. Astfel, cand programul care se executa ajunge la acea adresa, se da controlul rutinei de tratare pe nivelul 3.
Nivelele predefinite de intrerupere sunt, deci:
. 0 - depasire la impartire (cauze posibile; instructiunile DIV sau IDIV);
. 1 - executie pas cu pas (cauza posibila: bistabilul TF = 1;
. 2 - intrerupere externa nedezactivabila (cauza posibila: semnal electric pe linia de
intrerupere nedezactivabila NMI);
. 3 - executie pas cu pas (cauza posibila: instructiunea INT 3);
. 4 - depasire (cauza posibila: instructiunea INTO).
La calculatoarele de tip IBM-PC, se mai pot cita intreruperile hardware de la ceasul de timp real (nivelul 8) si de la tastatura (nivelul 9).
Intreruperile software in gama 20H - 2FH sunt folosite de sistemul de operare DOS, iar cele in gama 10H - 1AH de catre subsistemul de intrari - iesiri BIOS.
2.6 Instructiuni pentru controlul procesorului
Toate instructiunile de acest gen sunt fara operanzi.
O prima categorie de instructiuni se refera la controlul explicit al unor bistabili de conditie (CLC, STC, CMC, CLD, STD, CLI si STI).
Instructiunile CLC (Clear Carry Flag), STC (Set Cary Flag) si CMC (Complement Carry Flag) efectueaza operatiile de stergere, setare si, respectiv, complementare a bistabilului CF.
Instructiunile CLD (Clear Direction Flag) si STD (Set Dinction Flag) sterg, respectiv, seteaza bistabilul DF.
Instructiunile CLI (Clear Intrerrupt Flag) si STI (Set Interupt Flag) sterg, respectiv, seteaza bistabilul IF. Cand IF = 0, intreruperile externe nedezactivabile nu sunt luate in considerare. Astfel, o asa numita secventa critica de program (care nu dorim sa fie intrerupta) se protejeaza printr-o instructiune CLI inainte si o instructiune STI dupa. O secventa critica tipica este modificarea explicita a tabelei de intreruperi.
O alta serie de instructiuni (HALT, LOCK, WAIT) realizeaza unele operatii speciale asupra procesorului.
Astfel, instructiunea HALT (Oprire procesor) forteaza procesorul intr-o stare speciala de inactivitate, din care se poate iesi doar prin intreruperi externe sau prin reset general.
Prefixul LOCK (Blocare magistrala) se poate folosi inaintea oricarei instructiuni, efectul fiind ca pe durata instructiunii, accesul unui alt dispozitiv la magistrala sistemului hardware este blocat. intr-un sistem multiprocesor, nu este suficient sa protejam secventele critice prin instructiuni (LI/STI, deoarece controlul magistralei ar putea fi preluat de un alt procesor De aici, utilitatea prefixului LOCK.
Instructiunea WAIT (Asteapta) este folosita pentru a sincroniza activitatea procesorului cu cea a unui alt dispozitiv, de obicei un coprocesor matematic.
Executia unei instructiuni matematice este preluata de coprocesor; totusi, procesorul de baza nu poate continua programul pana nu primeste un semnal de la coprocesor, prin care se anunta sfarsitul operatiei executate. Aceasta sincronizare se realizeaza printr-un semnal electric aplicat pe linia TEST a procesorului de baza. Ca atare, instructiunea WAIT va forta procesorul de baza intr-o stare de inactivitate, pana la aparitia unui semnal electric pe linia TEST.
In fine, exista si o instructiune care nu face nimic: instructiunea NOP (No Operation). Scopul unei asemenea instructiuni este de a introduce o intarziere in program. Asamblorul recunoaste mnemonica NOP, dar codificarea interna este aceeasi cu a instructiunii XCHG AX, AX, care, evident, nu face nimic.
2.7 Dezvoltarea programelor in limbaj de asamblare
Acum, dupa ce am parcurs setul de instructiuni al procesorului, putem trece la prezentarea unui cadru de dezvoltare al programelor in limbaj de asamblare. Contextul ales este cel al calculatorului de tip IBM-PC sub sistemul de operare DOS si al produselor software Borland: asamblorul TASM (Turbo Assembler), link-editorul TLINK (Turbo Linker), bibliotecarul TLIB (Turbo Librarian) si depanatorul interactiv TD (Turbo Debugger).
Vom utiliza extensiile implicite ale fisierelor: .ASM pentru fisiere sursa, .OBJ pentru fisiere obiect, .EXE sau .COM pentru fisiere executabile.
O problema specifica limbajului de asamblare este absenta unor instructiuni de
Intrare - iesire de nivel inalt. Pentru asemenea operatii, va trebui sa ne scriem propriile rutine. In acest curs, vom utiliza o serie de proceduri si macroinstructiuni, implementate in fisierul IO.H si in fisierul IO.ASM. Aceste fisiere asigura (intre altele) suport pentru urmatoarele operatii de baza:
. introducerea de caractere de la consola;
. afisarea de caractere la consola;
. introducerea de siruri de caractere de la consola;
. afisarea de siruri de caractere la consola;
. afisarea unor mesaje imediate la consola;
. introducerea de intregi pe 16 biti cu sau fara semn de la consola;
. afisarea de intregi pe 16 biti cu sau fara semn la consola;
. initializarea registrelor DS si ES la intrarea in program;
. terminarea programului cu iesire in sistemul DOS. Se vor utiliza directivele de definire simplificata a segmentelor. Sablonul de dezvoltare al unui program ASM care contine un modul executabil va fi urmatorul:
.model MMMMM
include io.h
.stack NNNNN
.data
; Definitii de date
.code
; Definitii de proceduri
start:
init_ds_es
; Program principal
exit_dos
end start
unde:
. MMMMM este modelul de memorie, care poate fi tiny, small, medium, compact, large sau huge; modelele uzuale sunt small si large, in care toate adresele, procedurile, apelurile, salturile si revenirile din procedura sunt implicit de tip NEAR, respectiv de tip FAR;
. NNNN este dimensiunea rezervata segmentului de stiva; o valoare uzuala este 1024;
. include io.h este o directiva care include in textul sursa fisierul io.h, care trebuie sa se gaseasca in acelasi catalog (director) cu fisierul sursa;
. init_ds_es este o macroinstructiune (definita in io.h) care initializeaza registrele DS si ES cu adresa segmentului de date; registrele SS si CS sunt initializate automat la incarcarea programului executabil de pe disc.
. exit_dos este o macroinstructiune (definita in io.h) care provoaca terminarea programului si revenirea in sistemul DOS;
. start este o eticheta care marcheaza punctul de inceput al programului principal;
. end start este o directiva care precizeaza ca modulul curent este modul de
program principal, avand punctul de intrare !a eticheta start. O alta varianta este scrierea programului principal sub forma unei proceduri (avand numele, de exemplu, _main) si precizarea punctului de start prin numele procedurii:
.model MMMMM
include io.h
.stack NNNN
.data
; Definitii de date
.code
; Definitii de proceduri
_main proc
init_ds_es
; Program principal
exit_dos
_main endp
end _main
Un modul de program care nu este modul de program principal (deci contine definitii de date si / sau proceduri) nu are eticheta in directiva end. Intr-o aplicatie dezvoltata modular (in mai multe fisiere sursa), un singur modul poate fi modul de program principal.
Asamblorul nu face distinctie intre simboluri scrise cu litere mici sau mari. De acum incolo, vom scrie de regula programele cu litere mici, iar cu litere mari unele directive si tipuri de date definite de utilizator, pentru a le scoate in evidenta.
Asamblarea unui fisier sursa NUME.ASM se face cu una din comenzile:
C:> tasm nume.asm
C:> tasm nume
in urma careia rezulta un fisier obiect NUME.OBJ.
Daca se doreste si fisier listing, se da comanda:
C:> tasm nume ,,nume
in urma careia rezulta si un fisier NUME.LST.
Daca se doreste o depanare simbolica ulterioara, trebuie introdusa optiunea /zi:
C:> tasm nume /zi
Operatia de asamblare se face pentru fiecare fisier sursa in parte. Legarea modulelor se face cu comanda:
C:> tlink nume_1 nume_2 [, nume_bin ] [/v]
in care parantezele drepte indica parametrii optionali; nume_1, nume_2 sunt numele modulelor obiect, nume_bin este numele fisierului executabil rezultat (daca lipseste, se considera numele primului modul obiect), iar /v este o optiune de depanare simbolica (se introduce daca vrem sa executam programul sub controlul depanatorului TD). In urma comenzii, rezulta un fisier executabil.
O situatie tipica este cea a unui singur modul sursa, caz in care comanda de legare va fi:
C:> tlink nume io
prin care se leaga si modulul io.obj (care contine procedurile de intrare - iesire descrise mai sus).
Peste tot in cuprinsul cursului vom considera ca sirurile de caractere sunt terminate cu octetul 0 binar. Definitia unui sir constant sau rezervarea de spatiu pentru un sir variabil se pot face prin directiva Define Byte (constantele simbolice cr si lf sunt definite in fisierul io.h). Definitia unui intreg sau rezervarea de spatiu pentru un intreg se poate face cu directiva Define Word. Definirea de spatiu la nivel de caracter se face cu directiva Define Byte.
.data
sir_1 db 'Un sir de caractere', cr, lf, 0
sir_2 db 80 dup (0)
i_1 dw -200
i_2 dw ?
u_1 dw OFFFFH
u_2 dw -1
c_1 db 'A'
c_2 db ?
Afisarea unui sir de caractere la consola se poate face prin macroinstructiunea puts (Put String), in una din formele:
puts sir_1 ; O prima forma de invocare
lea si, sir_1 ; O a doua
puts [si] ; forma de invocare
Citirea unui sir de caractere de la consola se poate face cu macroinstructiunea gets (Get String), in una din formele:
gets sir_2 ; O prima forma de invocare
lea bx, sir_2 ; O a doua
gets [bx] ; forma de invocare
Afisarea unui sir constant de caractere (mesaj fa consola) se poate face cu macroinstructiunea putsi (Put String Immediate), care nu necesita definirea sirului si nici prezenta explicita a terminatorului 0:
putsi <'Introduceti un sir de caractere', cr, lf>
gets sir_2
putsi <'Sirul introdus este:', cr, lf>
puts sir_2
putsi <cr,lf>
Introducerea unui intreg cu sau fara semn se poate face cu macroinstructiunile geti (Get Integer), fara parametri, care intoarce intregul citit in registrul AX.
Afisarea unui intreg cu sau fara semn se poate face cu macroinstructiunile puti (Put Integer) sau putu (Put Unsigned), in una din formile:
geti ; Citeste intreg in AX
puti ax ; Afiseaza intregul din AX
mov i_1, ax ; Depune
puti i_1 ; Afiseaza intreg cu semn din memorie
putu u_1 ; Afiseaza ca numar fara semn
lea di, i_2
puti [di] ; Afiseaza ca numar cu semn
putu [di] ; Afiseaza ca numar fara semn
Introducerea unui singur caracter de la consola se poate face cu macroinstructiunea getc (Get Character), care intoarce caracterul in registrul AL, iar afisarea unui caracter se poate face cu macroinstructiunea putc (Put Character), in una din formele:
putc 'A'
putc car_1
lea bx, car_2
putc [bx]
Toate macroinstructiunile de mai sus conserva registrele procesorului, deci nu e nevoie de salvari si restaurari explicite.
Sa consideram un exemplu de program, in care se fac urmatoarele operatii:
. se citesc de la consola cel mult 20 de intregi cu semn;
. se afiseaza valorile introduse;
. se sorteaza crescator aceste valori;
. se afiseaza valorile sortate.
Consideram modelul de memorie large, adica toate adresele sunt implicit de 32 de biti. Pentru sortare, vom folosi un algoritm elementar (metoda bulelor), descris mai jos
(se considera ca indicii din tabloul a variaza de la 0 la n-1):
for i = 1 to n-1
for j = n - 1 downto i
if ( a[j-l] > a[j] )
schimba a[j] cu a[j-l]
Implementarea intregului program este urmatoarea:
.model large
include io.h
.stack 1024
. data
vec dw 20 dup (?)
n dw ?
. code
tipvec proc far
Procedura de afisare vector.
Date de intrare:
ds:si = adresa vector de intregi
cx = numar de elemente
jcxz tipend ; Nu avem ce afisa
tip:
puti [si] ; Afisare intreg cu semn
putsi <' '> ; Spatiu
add si, 2 ; Actualizare adresa
loop tip ; Bucla dupa CX
tipend:
ret
tipvec endp
bubble proc far
procedura de sortare
Date de intrare
ds:bx = adresa tablou de intregi
cx = dimensiune tablou
Variabila i : asignata la si
Variabila j : asignata la di
Adresa de inceput a tabloului: asignata la bx
cmp cx, 1
jbe algend ; Nu avem ce sorta
mov si, 1 ; i = 1
fori:
mov di, cx
dec di ; j = n-1
forj:
shl di, 1 ; intregii pe 2 octeti
mov ax, [bx][di-2] ; a[j-1]
cmp ax, [bx][di] ; Compara cu a[j]
jle nextj ; Mai mic sau egal
xchg ax, [bx][di] ; Schimba a[j]
mov [bx][di-2], ax ; cu a[j-1]
nextj:
shr di, 1 ; Refacere indice
dec di ; Ciclu downto
cmp di, si
jae forj ; Cat timp j >= i
nexti:
inc si ; Ciclu to
cmp si, cx
jb fori ; Cat timp i < n
algend:
ret
bubble endp
Programul principal
start:
init_ds_es
putsi <'Introduceti datele',cr,lf>
mov cx, 20 ; Numar maxim
;de elemente
lea bx, vec ; Adresa tablou
iar:
geti ; Citire intreg cu semn
test ax, ax ; Este 0 ?
jz gata ; Daca da, gata
mov [bx], ax ; Depunere in tablou
add bx, 2 ; Actualizare adresa
loop iar ; Bucla dupa CX
gata:
mov ax, 20 ; Calcul numar de
sub ax, cx ; elemente introduse
mov n, ax
putsi <'Vector nesortat', cr, lf>
lea si, vec ; Adresa tablou
mov cx, n ; Numar de elemente
call tipvec ; Afisare tablou
; nesortat
lea bx, vec ; Adresa tablou
mov cx, n ; Numar de elemente
call bubble ; Sortare tablou
putsi <cr,lf,'Vector sortat', cr, lf>
lea si, vec
mov cx, n
call tipvec ; Afisare tablou sortat
exit_dos ; iesire in DOS
end start
In segmentul de date, se rezerva spatiu pentru tabloul vec de cel mult 20 de intregi si pentru dimensiunea n a acestuia.
Procedura TIPVEC primeste in SI adresa unui tablou si in CX numarul de elemente, realizand afisarea la consola a elementelor, considerate intregi cu semn. Se observa forma standard a unei bucle de program implementata cu instructiunea LOOP.
Procedura BUBBLE implementeaza algoritmul de sortare. Indicii i si j din descrierea algoritmului sunt asignati la registrii SI si Dl. Indicii sunt incrementati sau decrementati cu 1, dar intregii sunt pe doi octeti. De aceea, in secventa de adresare a memoriei, se inmulteste temporar Dl cu 2, pentru a accesa corect elementele tabloului. Astfel, elementul de indice 0 se va afla la deplasament 0, elementul de indice 1 la deplasament 2 etc. Inmultirea si refacerea se realizeaza prin deplasari logice. Se observa secventa standard de interschimbare a doua elemente din memorie (doua MOV-uri si un XCHG). Trebuie remarcat modul in care se fac comparatiile diverselor elemente: indicii sunt considerati fara semn, deci testele se fac cu instructiuni de salt conditionat de tip "Above' sau "Below'. In schimb, tabloul este cu semn, deci comparatiile intre elemente se fac cu instructiuni de salt conditionat de tip "Greater' sau "Less'. Programul principal incepe printr-o bucla de citire a datelor. Introducerea se poate opri mai devreme de 20 de elemente daca se introduce valoarea 0. La iesirea din bucla, se calculeaza numarul de elemente efectiv introduse, ca diferenta dintre numarul maxim admis (20) si valoarea curenta a lui CX. Acest numar se depune in variabila n, pentru utilizari viitoare. In continuare, vom avea secvente de apel ale procedurilor TIPVEC si BUBBLE. Se observa incarcarea corespunzatoare a parametrilor (adresa tabloului si numarul de elemente) in registrele desemnate in acest scop pentru fiecare procedura. Exemplul prezentat ilustreaza modul de utilizare a macroinstructiunilor prezentate, ca si schema generala de dezvoltare a unui program.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 2808
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved