duminică, 28 octombrie 2012

Acces direct la memoria SRAM, Variabile, Operatii cu Variabilele


    Variabilă vom  numi un obiect ce conţine date care pot varia.
    Pentru limbajul ASM o variabilă vom numi o zonă de memorie alocată acestei variabile accesibile după un nume. Numele de variabilă este o eticheta în segmentul de date care reprezintă de fapt adresa către zona de memorie alocată variabilei. Spre deosebire de limbajele de nivel înalt, în ASM nu există tip al variabilei. O variabilă este caracterizată doar de numele acesteia, eticheta, şi volumul de memorie alocat acestei variabile.

    Declararea Variabilei.

    O variabilă se va declara în segmentul de date .DSEG şi va reprezenta o etichetă urmată de o directivă de preprocesare .BYTE care permite rezervarea unei zone de memorie pentru variabila dată.

 .DSEG            ; variabilele for fi declarate in 
                  ; segmentul de date
 var1 : .BYTE 1   ; declaratia variabilei var1 si rezervarea 
                  ; a 1 byte pentru aceasta variabila.
 var1 : .BYTE 2   ; declaratia variabilei var2 si rezervarea 
                  ; a 2 byte pentru aceasta variabila.




    Pentru a inţelege mai bine mecanismul de declarare a variabilelor în ASM vom prezenta o comparaţie cu   declaraţia de variabile în limbajul C++.

       C++     |     SIZE        |   ASM        
  char ch;     |  8 bit , 1 byte | ch: .BYTE 1
  int a;       | 32 bit , 4 byte |  a: .BYTE 4
  short int b; | 16 bit , 2 byte |  b: .BYTE 2
  long int c;  | 64 bit , 8 byte |  c: .BYTE 8
  float d;     | 32 bit , 4 byte |  d: .BYTE 4
  double e;    | 64 bit , 8 byte |  e: .BYTE 8




Operaţii de transfer cu variabilele.

    Operaţiile de transfer pentru variabile va fi executat cu comenzi de acces direct la memorie cum ar fi:
  • LDS R, k - încărcarea directă a valorii de la adresa k din SRAM într-un registru de uz general. 
  • STS k, R - stocarea directă a valorii unui registru de uz general la adresa k din SRAM.
    Etichetă - numele variabilei va reprezenta adresa primei locaţii a variabilei. Locatiile superioare după principiul "little-endian" se vor accesa după adresele imediat urmatoare. 

Atribuirea unei valori constante către o variabilă.

    Deorece nu există comanda de transfer a unei constante direct în SRAM, va trebui mai intai să încărcăm acea valoare intr-un registru de uz general după care transferată direct la adresa specificată de numele variabilei.

    ;EX: var1 = 25;
    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     var1 : .BYTE 1

    .CSEG               ; programul se scrie in segmentul de cod
        ldi r16, 25
        sts var1, r16    

    Pentru variabile de un volum mai mare de 1 byte atribuirea se va executa pentru fiecare byte separat. De menţionat eticheta-numele de variabilă este o adresă constantă. Luând în consideraţie că compilatorul evaluează operaţiile cu constantele în faza de pre-compilare adresa urmatorilor baiţi se poate forma prin adăugarea unui displasament la eticheta-nume,  <nume_var>+q  care este de fapt tot o constantă.

    ;EX: var2 = 2345;
    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     var2 : .BYTE 2

    .CSEG               ; programul se scrie in segmentul de cod
        ldi r16,    LOW(2345)
        sts var2,   r16    
        ldi r16,    HIGH(2345)
        sts var2+1, r16    

Transferul unei valori de la o variabila la alta.
    Asa cum pentru arhitectura AVR nu există o comandă de transfer de date direct dintr-o locaţie SRAM în alta, valoarea din locaţia de la adresa reprezentată prin numele variabilei sursa se va încărca mai întîi intr-un registru de uz general cu comanda LDS după care se va descărca direct în locaţia destinaţie cu STS.

    ;Ex: a = b;
    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     a : .BYTE 1        ; rezervare 1 byte pentru variabila a
     b : .BYTE 1        ; rezervare 1 byte pentru variabila b

    .CSEG               ; programul se scrie in segmentul de cod
        lds r16, b      ; incarcarea valorii variabilei 
                        ; b in registru de lucru
        sts   a, r16    ; descardcarea valorii registrului 
                        ; de lucru in variabila a

   Pentru variabile de un volum mai mare de un byte vom avea câte o operaţie de transfer pentru fiecare byte. Adresele baitilor superiori se vor forma din adresa de bază, eticheta-nume, şi constanta de deplasare care va indica numărul de ordine a byte-ului în variabilă.

    ;Ex: a = b;
    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     a : .BYTE 2        ; rezervare a 2 byte pentru variabila a
     b : .BYTE 2        ; rezervare a 2 byte pentru variabila b

    .CSEG               ; programul se scrie in segmentul de cod
        lds r16, b      ; citire byte inferior din sursa
        sts   a, r16    ; stocare byte inferior la destinatie
        lds r16, b+1    ; citire byte superior din sursa
        sts a+1, r16    ; stocare byte inferior la destinatie

Operaţii aritmetice cu variabile

    Pentru a evalua o operaţie artmetică între două variabile va trebui să transferăm mai întâi valorile variabilelor în regiştri de uz general după care vom avea posibilitatea de a evalua o operaţie aritmetică sau logică. Acest fapt se datorează restricţiilor arhitecturii care permite operaţii aritmetice doar cu regiştri de uz general.
         ;Ex: a = b + c;
    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     a : .BYTE 1        ; rezervare a 1 byte pentru variabila a
     b : .BYTE 1        ; rezervare a 1 byte pentru variabila b
     c : .BYTE 1        ; rezervare a 2 byte pentru variabila c

    .CSEG               ; programul se scrie in segmentul de cod
        lds r15, b      ; incarcarea valorii variabilei b
                        ; intr-un registru de lucru
        lds r16, c      ; incarcarea valorii variabilei c
                        ; in alt registru de lucru     
        add r15, r16    ; evaluarea operatiei de adunare
        sts   a, r15    ; descarcarea rezultatului in variabila a

    Acelaşi lucru vom face şi pentru variabile de o mărime de 2 byte. În acest caz  variabila poate fi încărcată într-o pereche de regiştri, de ex: [R15:R14]  = b. Pentru operaţiile între variabile mai mari de un byte va trebui să luăm în consideraţie transportul generat la operaţiile aritmetice între doi operanzi de 8 biţi.


          ;Ex: a = b + c;
    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     a : .BYTE 2        ; rezervare a 2 byte pentru variabila a
     b : .BYTE 2        ; rezervare a 2 byte pentru variabila b
     c : .BYTE 2        ; rezervare a 2 byte pentru variabila c

    .CSEG               ; programul se scrie in segmentul de cod
        lds r14, b      ; incarcarea primului byte a vartiabilei 
                        ; b in registru de lucru
        lds r15, b+1    ; incarcarea al 2-lea byte a vartiabilei 
                        ; b in registru de lucru

        ; incarcarea variabilei c intr-o pereche de registri
        lds r16, c
        lds r17, c+1

        add r14, r16   ; adunarea baiti inferiori
        adc r15, r17   ; adunarea baiti superiori luand in  
                       ; consideratie transpiortul de la 
                       ; operatia precedenta

        sts   a, r14   ; preluarea byte rezultat inferior 
        sts a+1, r15   ; preluarea byte rezultat superior 


    Vom încerca acelaşi lucru şi pentru variabile de mărimea de 4 byte.

          ;Ex: a = b + c;
    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     a : .BYTE 4        ; rezervare a 4 byte pentru variabila a
     b : .BYTE 4        ; rezervare a 4 byte pentru variabila b
     c : .BYTE 4        ; rezervare a 4 byte pentru variabila c

    .CSEG               ; programul se scrie in segmentul de cod
        ; incarcare variabila b in 4 registri de lucru
        lds r12, b
        lds r13, b+1
        lds r14, b+2
        lds r15, b+3

        ; incarcare variabila c in 4 registri de lucru
        lds r16, c
        lds r17, c+1
        lds r18, c+2
        lds r19, c+3

        ; operatia de adunare a 4 baiti
        add r12, r16
        adc r13, r17
        adc r14, r18
        adc r15, r19

        ; preluarea rezultatelor din 4 registri in variabila a         sts   a, r12    
        sts a+1, r13    
        sts a+2, r14    
        sts a+3, r15    

    Nu putem să nu observăm un exces de utilizare a regiştrilor de uz general pentru aceasta operaţie, 8 la număr. Dacă vom încerca să evaluăm o asemenea operaţie cu variabile de o mărime mai mare, de ex. 8 baiti, vom implica în această operaţie 16 regiştri. Vom remarca că această metodă, de a încărca variabilele în întregime în regiştri nu este eficientă.
    Luând în consideraţie că operaţiile de transfer nu modifică flaguri în registrul de stare SREG, am putea să încărcăm în regiştri pe rând baiţii de acelaşi ordin şi execută operaţiile. Stocarea byte-ului curent nu va modifica flagul de transport C, la fel ca şi operaţiile de încărcare a urmatorului byte, şi respectiv putem aplica comenzi ce vor lua în consideraţie transportul de la operaţia aritmetică precedentă. În aşa mod în operaţie vor fi implicaţi doar doi regiştri de uz general. Mai jos vom aplica metoda optimizată descrisă pentru operaţia cu variabile de 4 baiţi.

.         ;Ex: a = b + c;

    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     a : .BYTE 4        ; rezervare a 4 byte pentru variabila a
     b : .BYTE 4        ; rezervare a 4 byte pentru variabila b
     c : .BYTE 4        ; rezervare a 4 byte pentru variabila c

    .CSEG               ; programul se scrie in segmentul de cod
        lds r12, b      ; incarcarea primului byte al variabilei 
                        ; a in registrul de lucru
        lds r13, c      ; incarcarea primului byte al variabilei 
                        ; c in registrul de lucru
        add r12, r13    ; adunarea primului byte
        sts   a, r12    ; preluarea primului byte al rezultatului
   
        ; evaluarea operatiei pentru al 2-lea byte
        lds r12, b+1
        lds r13, c+1
        adc r12r13    ; adunare cu transport
        sts a+1, r12

        ; evaluarea operatiei pentru al 3-lea byte
        lds r12, b+2
        lds r13, c+2
        adc r12r13
        sts a+2, r12
  
        ; evaluarea operatiei pentru al 4-lea byte
        lds r12, b+3
        lds r13, c+3
        adc r12r13
        sts a+3, r12


Variabile ca parametri de intrarea in subrutina

    În cazul în care se doreşte o evaluare a unei variabile cu ajutorul unei subrutine, înainte de a apela subrutina va fi nevoie de a transfera conţinutul variabilelor în regiştrii utilizaţi ca parametri de intrare a subrutinei, iar după returnarea din subrutină rezultatul se va transfera în variabila destinaţie.


 ;Ex: a = MySub(b);
    .DSEG               ; variabilele for fi declarate in 
                        ; segmentul de date
     a : .BYTE 1        ; rezervare a 1 byte pentru variabila a
     b : .BYTE 1        ; rezervare a 1 byte pentru variabila b

    .CSEG               ; programul se scrie in segmentul de cod
        push r16        ; salvarea registrului parametru
        lds r16, b      ; initializarea paramentrului cu 
                        ; valoarea variabilei
        rcall MySub     ; apelul subrutinei
        sts   a, r16    ; extragerea rezultatului
        pop r16         ; restabilirea registrului paramentru