logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[Atmega8][AVR-GCC] - przesiadka z ASM na C

JollyRoger 06 Mar 2011 14:48 2252 9
  • #1 9242538
    JollyRoger
    Poziom 21  
    Witam, po dłuższej przygodzie z Asemblerem postanowiłem zapoznać się z C dla procesorów AVR. Ku mojemu zdziwieniu prosty kod taki jak:
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    int main(void)
    {
    		// ustawienie trybu pracy licznika
    	TCCR0 = 0x04;
    	// wpisanie wartości początkowej
    	TCNT0 = 254;
    	// odblokowanie przerwania od licznika
    	TIMSK = 0x01;
    	// globalne odblokowanie przerwań
    	sei();
    	PORTB = 0x55;
    	
    	for(;;){asm volatile("nop"::);}
    }
    
    SIGNAL (SIG_OVERFLOW0)
    {
    	TCNT0 = 254;
    	static char a=0;
    	PORTB = a;
    	a++;
    }
    

    Zmieniany jest w taki kod:
    
    +00000000:   C012        RJMP      PC+0x0013      Relative jump
    +00000001:   C021        RJMP      PC+0x0022      Relative jump
    +00000002:   C020        RJMP      PC+0x0021      Relative jump
    +00000003:   C01F        RJMP      PC+0x0020      Relative jump
    +00000004:   C01E        RJMP      PC+0x001F      Relative jump
    +00000005:   C01D        RJMP      PC+0x001E      Relative jump
    +00000006:   C01C        RJMP      PC+0x001D      Relative jump
    +00000007:   C01B        RJMP      PC+0x001C      Relative jump
    +00000008:   C01A        RJMP      PC+0x001B      Relative jump
    +00000009:   C025        RJMP      PC+0x0026      Relative jump
    +0000000A:   C018        RJMP      PC+0x0019      Relative jump
    +0000000B:   C017        RJMP      PC+0x0018      Relative jump
    +0000000C:   C016        RJMP      PC+0x0017      Relative jump
    +0000000D:   C015        RJMP      PC+0x0016      Relative jump
    +0000000E:   C014        RJMP      PC+0x0015      Relative jump
    +0000000F:   C013        RJMP      PC+0x0014      Relative jump
    +00000010:   C012        RJMP      PC+0x0013      Relative jump
    +00000011:   C011        RJMP      PC+0x0012      Relative jump
    +00000012:   C010        RJMP      PC+0x0011      Relative jump
    +00000013:   2411        CLR       R1             Clear Register
    +00000014:   BE1F        OUT       0x3F,R1        Out to I/O location
    +00000015:   E5CF        LDI       R28,0x5F       Load immediate
    +00000016:   E0D4        LDI       R29,0x04       Load immediate
    +00000017:   BFDE        OUT       0x3E,R29       Out to I/O location
    +00000018:   BFCD        OUT       0x3D,R28       Out to I/O location
    +00000019:   E010        LDI       R17,0x00       Load immediate
    +0000001A:   E6A0        LDI       R26,0x60       Load immediate
    +0000001B:   E0B0        LDI       R27,0x00       Load immediate
    +0000001C:   C001        RJMP      PC+0x0002      Relative jump
    +0000001D:   921D        ST        X+,R1          Store indirect and postincrement
    +0000001E:   36A1        CPI       R26,0x61       Compare with immediate
    +0000001F:   07B1        CPC       R27,R17        Compare with carry
    +00000020:   F7E1        BRNE      PC-0x03        Branch if not equal
    +00000021:   D002        RCALL     PC+0x0003      Relative call subroutine
    +00000022:   C020        RJMP      PC+0x0021      Relative jump
    +00000023:   CFDC        RJMP      PC-0x0023      Relative jump
    @00000024: main
    ---- nuda.c ---------------------------------------------------------------------------------------
    5:        {
    +00000024:   E084        LDI       R24,0x04       Load immediate
    +00000025:   BF83        OUT       0x33,R24       Out to I/O location
    9:        	TCNT0 = 254;
    +00000026:   EF8E        LDI       R24,0xFE       Load immediate
    +00000027:   BF82        OUT       0x32,R24       Out to I/O location
    11:       	TIMSK = 0x01;
    +00000028:   E081        LDI       R24,0x01       Load immediate
    +00000029:   BF89        OUT       0x39,R24       Out to I/O location
    13:       	sei();
    +0000002A:   9478        SEI                      Global Interrupt Enable
    14:       	PORTB = 0x55;
    +0000002B:   E585        LDI       R24,0x55       Load immediate
    +0000002C:   BB88        OUT       0x18,R24       Out to I/O location
    16:       	for(;;){asm volatile("nop"::);}
    +0000002D:   0000        NOP                      No operation
    +0000002E:   CFFE        RJMP      PC-0x0001      Relative jump
    @0000002F: __vector_9
    20:       {
    +0000002F:   921F        PUSH      R1             Push register on stack
    +00000030:   920F        PUSH      R0             Push register on stack
    +00000031:   B60F        IN        R0,0x3F        In from I/O location
    +00000032:   920F        PUSH      R0             Push register on stack
    +00000033:   2411        CLR       R1             Clear Register
    +00000034:   938F        PUSH      R24            Push register on stack
    21:       	TCNT0 = 254;
    +00000035:   EF8E        LDI       R24,0xFE       Load immediate
    +00000036:   BF82        OUT       0x32,R24       Out to I/O location
    23:       	PORTB = a;
    +00000037:   91800060    LDS       R24,0x0060     Load direct from data space
    +00000039:   BB88        OUT       0x18,R24       Out to I/O location
    24:       	a++;
    +0000003A:   5F8F        SUBI      R24,0xFF       Subtract immediate
    +0000003B:   93800060    STS       0x0060,R24     Store direct to data space
    25:       }
    +0000003D:   918F        POP       R24            Pop register from stack
    +0000003E:   900F        POP       R0             Pop register from stack
    +0000003F:   BE0F        OUT       0x3F,R0        Out to I/O location
    +00000040:   900F        POP       R0             Pop register from stack
    +00000041:   901F        POP       R1             Pop register from stack
    +00000042:   9518        RETI                     Interrupt return
    25:       }
    +00000043:   94F8        CLI                      Global Interrupt Disable
    +00000044:   CFFF        RJMP      PC-0x0000      Relative jump
    

    Zupełnie nie rozumiem:
    - do czego są użyte rejestry r0, r1 i dlaczego są odkładane na stosie?
    - po co używana jest pamięć RAM (ST,LD) skoro mamy aż 32 rejestry i można by wartość zmiennej "a" trzymać w jednym z rejestrów?
    - dlaczego obsługa przerwania nie jest na końcu kodu tylko później są jeszcze dwa rozkazy?
  • #2 9242713
    tadzik85
    Poziom 38  
    Wykorzystanie rejestrów opisane jest w dokumentacji AVR-GCC.

    R1 to rejestr zerowy który wg standardu zawsze ma mieć wartość 0.
    GCC zmienną zawsze umieszcza w RAM. Rejestry służą do innych celów.

    Te ostatnie 2 rozkazy to nieskończona pętla. Zabezpieczenie przed pójściem softu tam gdzie nie powinien ;p.
  • #3 9242723
    mirekk36
    Poziom 42  
    A jak pisałeś w asemblerze to obsługa przerwania musiała być na końcu? ;) .... no nie musi być - może być gdzie zechcesz.

    A dlaczego tak skompilowany jest prosty program w C ??? to trzeba byłoby poczytać gdzieś jak działa taki kompilator, co on wstawia , na jakie sekcje dzieli pamięci ram itp .... no jest tego trochę do poczytania.

    Bardzo ogólnie mówiąc , kompilator C przy starcie programu a w zasadzie przed startem umieszcza swoje różne fragmenty programu do inicjalizacji właśnie obszarów pamięci RAM, zmiennych globalnych, kilka różnych sekcji startowych INIT, itp ....

    Generalnie pisząc w C nie musisz się już martwić o takie podstawowe rzeczy, które trzeba było samemu dłubać w asm ... ;) i tu jest jedna z pierwszych ogromnych zalet języka C - bo szalenie ułatwia pracę pisanie i powstawanie programów
  • #4 9242935
    JollyRoger
    Poziom 21  
    tadzik85 napisał:

    GCC zmienną zawsze umieszcza w RAM. Rejestry służą do innych celów.

    Czyli za każdym razem tracę min. 2 rozkazy ze względu na specyfikę GCC - trochę mnie to nie przekonuje. np. ATtiny13 (najprawdopodobniej) nie ma ramu i ma tylko 3 poziomowy, sprzętowy stos, jednak nie przeszkadza to w obliczeniach z ADC.
    W swoich programach pisanych w ASM zazwyczaj zmienne trzymam w rejestrach (szczególnie tych poniżej r16) i gdy jest potrzeba to przekładam je na stos w jakiejś procedurze - przyspiesza to pracę i odchudza kod - czy można podobnie skonfigurować GCC (jakieś flagi kompilatora itp)?

    Podobnie z SREG w przykładowym programie nawet nie ma potrzeby zapamiętywania jego stanu w przerwaniu - program i tak wróci do głównej pętli a tam żadne rozkazy nie odwołują się do SREG :).
    Cytat:

    Generalnie pisząc w C nie musisz się już martwić o takie podstawowe rzeczy, które trzeba było samemu dłubać w asm ... Wink i tu jest jedna z pierwszych ogromnych zalet języka C - bo szalenie ułatwia pracę pisanie i powstawanie programów
    może na komputery czy jakieś większe procesory gdzie można dołączyć zewnętrzną pamięć programu jest to wygodne i ułatwia pracę, ale są momenty gdzie w ATmega8 czy ATtiny2313 każdy bajt jest na wagę złota ;)
  • #5 9242984
    tadzik85
    Poziom 38  
    AVR-GCC doskonale zarządza zmiennym. Na czas używania jest ona przechowywana w rejestrze.

    SREG może w tym przypadku nie jest konieczne jego zapamiętywanie ale jak będzie konieczne? Taki jest standard. Sreg jest zawsze zapamiętywany. Dlaczego nie muszę ci tłumaczyć. Zamiast zastanawiać się jak ustawić kompilator przyzwyczaj się i przejrzyj dokumentacje AVR-GCC.
  • #6 9243075
    tmf
    VIP Zasłużony dla elektroda
    Kompilator umieścił twoją zmienną w rejestrze bo sam to wymusiłeś dodając modyfikator static. Nie analizuj też działania kompilatora na trywialnych programach. Jak myślisz, czy twoje założenia o niemodyfikowaniu SREG, czy umieszczaniu zmiennych w rejestrach są słuszne dla nietrywialnych przykładów? Zdecydowanie nie. A zmienne możesz trzymać w rejestrach stosując modyfikator register.
    Z drugiej strony po co ten wątek? Chcesz się czegoś nauczyć, czy przekonać innych, że C jest be? Bo jeśli to drugie to daruj sobie.
  • #7 9243178
    JollyRoger
    Poziom 21  
    tadzik85 napisał:
    Na czas używania jest ona przechowywana w rejestrze.

    To chyba oczywiste - na Tiny czy Mega nie ma rozkazów operujących bezpośrednio na zmiennych w pamięci.

    tadzik85 napisał:

    SREG może w tym przypadku nie jest konieczne jego zapamiętywanie ale jak będzie konieczne?

    To kompilator przerzuci SREG na stos lub do rejestru.

    Podam prosty przykład: jak w C dla ATTiny2313 (128B SRAM) napisać program który
    w komórkach 0...125 ma dane i odczytuje je na PORTB po kolej w przerwaniu?


    tmf napisał:
    Chcesz się czegoś nauczyć, czy przekonać innych, że C jest be? Bo jeśli to drugie to daruj sobie.

    Nie mam sobie czego darować. Wątek powstał po to gdyż mam gotowe urządzenie z ATmega8 i dla celów dydaktycznych chcę je z ASM przepisać na C. Kod nie jest skomplikowany ale właśnie chodzi m.in o pamięć . Zajęty jest prawie cały RAM ( wolne są tylko 3 poziomy na stosie) a jako zmienne licznikowe (jak w przykładzie) służą rejestry.
    Ale porady typu
    Cytat:
    Zamiast zastanawiać się jak ustawić kompilator przyzwyczaj się
    nie ułatwiają tego. Wiem że są różne flagi sterujące kompilacją , różne stopnie optymalizacji, ale nie znalazłem gotowej odpowiedzi na moje pytanie.
  • #8 9243307
    tmf
    VIP Zasłużony dla elektroda
    Nie? A czytałeś to co ci napisałem? Chyba nie.
    Chcesz powiedzieć, że zużyłeś cały 1kB SRAM w ATMega8? Jak rozumiem poprzez zadeklarowanie zmiennych globalnych/statycznych o mniej więcej takim rozmiarze?
  • Pomocny post
    #9 9243581
    Moyshaa
    Poziom 14  
    Jeśli twoja funkcja nie zmienia SREG, to dopisz funkcji atrybut naked i kompilator nie dopisze jej prologu, ani epilogu... Na wszystko są sposoby. Znajomość asm powoduje, że szybciej opanujesz te triki i zrozumiesz ich działanie. No, ale do tego potrzeba trochę pokory i dobrą literaturę. Na początek możesz też sobie jeszcze raz przeczytać co napisał tmf.
REKLAMA