miercuri, 26 septembrie 2012

Iniţiere în programare ASM. Stiva. Subrutina. Subrutina cu parametri.


    Initiere in programare ASM

    Entitatea de baza a limbagului ASM este comanda.
    Vom numi program o consecutivitate de instructiuni (comenzi) ce se executa una dupa alta.
   Programarea in limbajul ASM reprezinta accesul la memorie si modificarea volorilor la aceste adrese utilizand setul de comenzi. Limbajul permite gruparea secventelor de comenzi in subrutine, executia conditionata a secventelor de program realizata in baza salturilor conditionate si neconditionate.

    Structura unui program in ASM

    Un microcontroller in orice moment de timp trebuie sa execute o instructiune definita de programator. Atunci cand microcontrollerul executa o comanda definita de programator vom spune ca controllerul se afla sub controlul programului.Si invers, In cazulin care se executa o comanda care nu a fost definita de catre programator vom spune ca microcontrollerul a iesit de sub control.
    Un program ce ruleaza pe microcontroller trebuie sa mentina microcontrollerul intotdeauna sub control ceia ce presupune existenta unei bucle infinite in program.

    Vom defini doua parti componente principale a unui program:

  • Partea de initializare - o secventa de cod la inceputul programului ce serveste pentru initializarea microcontrollerului pentru lucru.
  • Partea de procesare - rezolvarea problemei si mentinerea sub control a Microcontrollerului cu o bucla infinita.
    Ca urmare a celor spuse mai sus un program in ASM va avea urmatoarea structura:

init:               ; punctul de intrare in program
    ...
    ...
    ...             ; partea de initializare
    ...             ; a microcontrollerului
    ...
end_init:           ; indicator de sfarsit al initializarii
main_loop:          ; inceputul partii de procesare
    ...
    ...             ; corpul partii de procesare
    ...             ; inclus in bucla infinita
    ...
end_main_loop:      ; sfarsitul partii de procesare    
    rjmp main_loop  ; salt spre inceputul buclei infinite


     Pentru evidentierea zonelor de initializare si procesare a programului se recomanda utilizarea etichetelor (label).

    Directive de preprocesare a compilatorului.

    Directive de preprocesare a compilatorului sunt comenzi catre compilator care sunt executate de catre compilator inaintea generarii codului binar. rolul lor este de a configura compilatorul si a indica compilatorului modul in care trebuie sa fie tratate liniile de program care le urmeaza.
    In aceasta faza vom defini cateva directive de preprocesare a compilatorului:

.include <fisier> - indica compilatorului sa includa in locul in care e invocata comanda fisierul respectiv
.cseg - indica compilatorului ca liniile de cod care urmeaza aceasta directiva fac parte din segmentul de memorie de program
.org <addr> - indica compilatorului ca liniile de cod care urmeaza se vor inregistra incepand de la o adresa specificata in sgmentul de emorie  curent


.include "m16def.inc" ; includem fisierul de adrese la periferice
                      ; pentru ATmega16
.cseg                 ; inicam inceputul segmentului de cod
.org 0                ; programul va fi inregistrat de la 
                      ; adresa 0x00
init:                 ; punctul de intrare in program
    ...
    ...

    Stiva (Stack)

   Stiva este un mecanism care permite stocarea temporara a datelor dupa principiul LIFO (Last Input First Output) - Primul Intrat Ultimul Iesit.
    Introducerea in stiva se va executa prin comanda PUSH R - introducerea continutului unui Registru de Uz General in Stiva (R -> STACK). Extragerea datelor cu comanda POP R (R <- STACK).
    Deasemenea stiva este un mecanism fara de care nu este posibil lucrul cu subrutinele, deoarece mecanismul stivei permite stocarea temporara a Contorului de Program PC pe durata executiei subrutinei. Respectiv comenzile de tip CALL si RET deasemenea vor introduce/extrage date din stiva.
    Mecanismul de stiva este realizat in baza memoriei RAM si un Registru Indicator de Stiva - SP (Stack Pointer).
    Indicatorul de stiva SP indica la adresa curenta de scriere in spatiul RAM.
    SP este un registru dublu (16 biti), construit in baza  [ SPH : SPL ]  facilitand accesul la intreg spatiul de memorie, pana la 64 K.
    Executand comanda PUSH R continutul registrului se va inregistra la adresa ce se contine in registrul Indicator de Stiva SP, dupa care are loc decrementarea SP.

  • PUSH R
    1. RAM[SP] = R
    2. SP = SP - 1 

    Pentru comanda POP R executia presupune Incrementarea Indicatorului de stiva SP urmata de transferul in Registrul de Uz General a valorii de la adresa SP

  • POP R
    1. SP = SP + 1
    2. R = RAM[SP]
    Adaugarea elementelor in stiva va implica cresterea stivei. reesind din cele spuse mai sus, vom afirma ca Stiva creste in directia diminuarii adreseilor memoriei RAM.

    Initializarea Stivei

    Registrul Indicator de Stiva SP este localizat in spatiul registrilor periferici, respectiv citirea si modificarea valorii din SP se va efectua prin comenzile IN si OUT.
    Deoarece valoarea indicatorului de stiva SP descreste odata cu cresterea stivei, se recomanda initializarea stivei la o adresa avansata in spatiul de memorie RAM. O asemenea valoare pote fi constanta RAMEND care contina ultima adresa existenta din memoria RAM interna a Microcontrollerului.
    Operatia de Initializare a stivei va consta in initializarea Indicatorului de stiva SP.

    SP = RAMEND,         [SPH:SPL] = RAMEND

Secventa de cod in ASM pentru operatia de mai sus va arata in felul urmator:

       ldi R16, LOW(RAMEND)    
       out SPL, R16          ; initializare byte inferior SP
       ldi R16, HIGH(RAMEND)   
       out SPH, R16          ; initializare byte superior SP 

unde:
  • LOW - macroinstructiunea pentru selectarea byte-ului inferior dintr-o constanta.
  • HIGH - macroinstructiunea pentru selectarea byte-ului superior dintr-o constanta.
    Intr-un ciclu de program numarul de inregistrari trebuie sa fie egal cu numarul de extrageri pentru a evita erori de scurgere sau violare de memorie. 

    Subrutina

    Subrutina - Secventa de instructiuni grupata intre o eticheta (label) si instructiune de returnare din subrutina RET. Eticheta reprezinta o adresa in memoria de programe fiindui atribuit un nume de acces. In program va fi reprezentata de un nume de eticheta urmata de (:)

MySub:      ; Numele subrutinei reprezentata de  
            ; eticheta inceputui subrutinei
    ...
    ...     ; Corpul subrutinei
    ...
    ...
    ret     ; returnare din subrutina

    Subrutina poate fi apelata prin numele ei cu una din comanzile de chemare subrutinei CALL, RCALL sau ICALL.

    ...
    ...
    rcall MySub  ; apelul subrutinei MySub
    ...
    ...
  
    Mecanismul de chemare a subrutinei consta in salvarea adresei curente de executie in stiva pentru a avea posibilitatea de a restabili executia comenzilor urmatoare dupa returnarea din subrutina.
    Instructiunea de apel a subrutinei cu o instructiune de til CALL ca fi interpretata dupa cum urmeaza:
    1. PC -> STACK - salvarea adresei curente in Stiva
    2. jmp MySub      - salt la adresa etichetei My Sub
   Pentru returnare din subrutina vom avea:
    1. PC <- STACK - restabilirea adresei din stiva
    2. PC <- PC + 1   - trecerea la comanda urmatoare dupa comanda de apel a subrutinei
    De mentionat ca Contorul de Program PC este un registru de 16 biti, respectiv va a ocupa doua locatii in stiva, executanduse doua operatii de salvare a unui byte in stiva.
   Deoarece un apel de subrutina presupune utilizarea stivei. este strict recomandat ca inainte de un oarecare prim apel stiva sa fie initializata, iar locul cel mai potrivit este in partea de initializare a programului.


.include "m16def.inc" ; includem fisierul de adrese la periferice
                      ; pentru ATmega16
.cseg                 ; inicam inceputul segmentului de cod
.org 0                ; programul va fi inregistrat de la 
                      ; adresa 0x00
init:                 ; punctul de intrare in program
    ldi R16, LOW(RAMEND) ; initializarea stivei
    out SPL, R16
    ldi R16, HIGH(RAMEND)
    out SPH, R16
    ...


    Subrutina securizata

    Vom numi subrutina securizata subrutina care nu afecteaza valorile registrilor prin executia sa. Mecanismul subrutinei securizate consta in salvarea registrilor utilizati in subrutina in stiva la inceputul subrutinei si restabilirea valorilor registrilor inainte de returnare din subrutina.


MySub:          ; Numele subrutinei reprezentata de  
                ; eticheta inceputui subrutinei
    push r15    ; salvarea registrului r15
    push r16    ; salvarea registrului r16
    ...
    ...         ; Corpul subrutinei
    ...         ; utilizare registri r15 si r16
    ...
    ...
    pop r16     ; restabilire r16
    pop r15     ; restabilier r16
    ret         ; returnare din subrutina

De mentionat ca ordinea restabilirii registrilor de lucru este inversa celei de salvare, conform principiului LIFO al stivei.

Subrutina cu parametri

    In calitate de parametri al unei subrutine pot fi utilizati registrii de uz general. In asa mod pentru a transmite date date de intrare in subrutina, inainte de a apela o subrutina este nevoie ca registrii utilizati ca parametri sa fie initializati cu valorile de intrare dorite pentru acesti parametri. Iar dupa executia subrutinei se vor prelua valorile din registrii rezervati parametrulilor de iesire.
    De exemplu daca vom avea o subrutina care va utiliza R16 si R17 ca parametri de intrare si R18 ca parametru de iesire, in care subrutina va stoca rezultatul. si de exemplu vom avea de evaluat o expresie de tipul:
           a = MySub(K1, K2)
Apelul de subrutina va arata in felul urmator:
    ...
    ...
    ldi r16, K1  ; initializarea primului paramatru
    ldi r17, K1  ; initializarea parametrului al doilea
    rcall MySub  ; apelul subrutinei MySub
    sts a, r18   ; preluarea rezultatului subrutinei
    ...
    ...

    Registrii utilizati ca parametri de intrare/iesire NU vor fi salvati in interiorul subrutinei. mecanismul de salvare a registrilor utilizati ca parametri se va implementa dupa cum urmeaza: paramtrii se vor salva in stiva inainte de initializare si se vor restabili dupa preluarea rezultatului subrutinei.

    ...
    ...
    push r16     ; salvarea registrului parametru r16
    push r17     ; salvarea registrului parametru r17
    push r18     ; salvarea registrului parametru r18
    ldi r16, K1  ; initializarea primului paramatru
    ldi r17, K1  ; initializarea parametrului al doilea
    rcall MySub  ; apelul subrutinei MySub
    sts a, r18   ; preluarea rezultatului subrutinei
    pop r18      ; restabilirea registrului parametru r16
    pop r17      ; restabilirea  registrului parametru r17
    pop r16      ; restabilirea  registrului parametru r18
    ...
    ...
    Inca odata reamintim ca ordinea restabilirii registrilor va fi inversa celei de salvare.