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

[AVR Studio] Dziwny problem z przesunieciem bitowym

bajerybajery 19 Wrz 2008 12:24 3465 14
  • #1 5549898
    bajerybajery
    Poziom 10  
    Dzień dobry,

    mam mały kłopot z przesunięciem bitowym na AVR Studio. Otóż w pewnym miejscu programu, chce sprawdzić, czy na n-tym miejscu liczby stoi jedynka czy zero (binarnie). Więc napisałem:

    
    ...
    przes = szerokosc_matrycy-x-1;
    potega = 1<<przes;
    if ((Matryca[y]&potega)!=potega)
    {
    ...
    


    gdzie x zmienia się od 0 do 4, a y od 0 do 6, natomiast wszystkie zmienne i tablice w tym przykładzie są typu uint32_t. Teraz jaki jest problem. Otóż gdy program dochodzi do linijki w której ma nastąpić przesunięcie (wartość zmiennej 'przes' jest poprawnie obliczana) wyłania się dziwny błąd, ponieważ obliczona wartość przesunięcia (zapisana w zmiennej 'potega') jest zawsze równa 0. Najciekawsze jest to, że gdy tę samą liczbę (też jedynkę) przesuwam o tę samą ilość miejsc na Windowsowym kalkulatorze - wynik jest poprawny. Przeniosłem więc program do C++ Builder - tam działa prawidłowo. Napisałem też krótki program w Delphi - też przesuwa prawidłowo.
    Wpadłem więc na pomysł napisania komplementarnej funkcji potęgującej liczbę 2 (odpowiednik przesunięcia bitowego w lewo).

    
    uint32_t Poteguj2(char x)
    {
    	uint32_t i,wynik;
    	wynik = 1;
    	for(i=1;i<=x;i++)
    		wynik = wynik * 2;
    	
    	return wynik;
    }
    


    No i oczywiście zamiast linijki:
    potega = 1<<przes;

    wstawiłem:
    potega = Poteguj2(przes);


    Program zaczął działać, tyle, że (co zrozumiałe) dużo wolniej. Kto mi powie co jest grane? Dodam, że optymalizacje kompilatora wyłączyłem!
  • #3 5549982
    bajerybajery
    Poziom 10  
    Zmienna 'potega' jest również typu uint32_t , wartości przesunięcia występują w zakresie od zera do 19 i w żaden sposób nie powinny wyjść poza zakres tego typu zmiennej.
  • #4 5550018
    szelus
    Poziom 34  
    bajerybajery napisał:
    Dodam, że optymalizacje kompilatora wyłączyłem!

    Tzn. masz opcję -O0?

    Z informacji, które przedstawiłeś wygląda, że powinno działać jak należy. Ponieważ jednak nie działa, a kod nie jest jakoś szczególnie skomplikowany/nietypowy, to wynika z tego, że nie przedstawiłeś całej istotnej informacji.
    Niestety, tylko Ty posiadasz tą informacje, więc nam trudno zgadywać...
  • #5 5550020
    Freddie Chopin
    Specjalista - Mikrokontrolery
    zakladajac ze AVR studio, a w zasadzie gcc, przeznaczone dla 8-bitowej architektury w ogole jest w stanie skumac to, ze ma przesuwac cos o wiecej niz 8 bitow...

    gcc dla 16bitowych PICow jest w stanie prawidlowo przesuwac zmienne 32-bitowe, ale kto wie jak to w avrach jest <:

    wrzuc no listing assemblerowy tego kawalka kodu. sprawdz tez, czy jak dasz zmienna 0x55555555 i zrobisz na niej <<1 to wyjdzie ci prawidlowe 0xAAAAAAAA

    4\/3!!
  • #6 5550054
    bajerybajery
    Poziom 10  
    Chciałem Wam bardzo podziękować za zainteresowanie i profesjonalne odpowiedzi. Dziękuję!

    Co do Assemblera - patrzyłem na jego kod. W pewnym momencie następuje pętla w której następuje 'przes'-krotne przesunięcie bitowe w lewo (za każdym razem o jeden - rozkaz LSL), następnie w pamięci umieszczana jest dziwna wartość rejestru R24 (nie wiem czy dobrze czytam kod):

    
    8F8E        STD     Y+30,R24
    


    .. później to miejsce w pamięci jest zerowane:
    
    A3A8        STD     Y+32,R26
    


    Assemblerowy kod instrukcji przesunięcia:

    372:      		potega = 1<<przes;
    +00000104:   8D2A        LDD     R18,Y+26         Load indirect with displacement
    +00000105:   8D3B        LDD     R19,Y+27         Load indirect with displacement
    +00000106:   E081        LDI     R24,0x01         Load immediate
    +00000107:   E090        LDI     R25,0x00         Load immediate
    +00000108:   C002        RJMP    PC+0x0003        Relative jump
    +00000109:   0F88        LSL     R24              Logical Shift Left
    +0000010A:   1F99        ROL     R25              Rotate Left Through Carry
    +0000010B:   952A        DEC     R18              Decrement
    +0000010C:   F7E2        BRPL    PC-0x03          Branch if plus
    +0000010D:   27AA        CLR     R26              Clear Register
    +0000010E:   FD97        SBRC    R25,7            Skip if bit in register cleared
    +0000010F:   95A0        COM     R26              One's complement
    +00000110:   2FBA        MOV     R27,R26          Copy register
    +00000111:   8F8E        STD     Y+30,R24         Store indirect with displacement
    +00000112:   8F9F        STD     Y+31,R25         Store indirect with displacement
    +00000113:   A3A8        STD     Y+32,R26         Store indirect with displacement
    +00000114:   A3B9        STD     Y+33,R27         Store indirect with displacement



    Co do eksperymentu - wychodzi prawidłowo 0xAAAAAAAA z tego wynika, że kompilator radzi sobie z takimi liczbami.


    Co do przedmówcy:
    Cytat:
    Tzn. masz opcję -O0?

    Tak.

    Cytat:

    Z informacji, które przedstawiłeś wygląda, że powinno działać jak należy. Ponieważ jednak nie działa, a kod nie jest jakoś szczególnie skomplikowany/nietypowy, to wynika z tego, że nie przedstawiłeś całej istotnej informacji.


    Nie za bardzo wiem, co jeszcze podać. Chodzi o to, że jedna linijka programu jest po prostu nieprawidłowo wykonywana.
  • Pomocny post
    #7 5550082
    szelus
    Poziom 34  
    No tak, wszystko jasne...
    Powinno być:
    
    potega = 1L << przes; 
    

    Sądzac z kodu wygenerowanego przez kompilator zmienna przes jest typu unsigned int. Domyślnym rozmiarem jest 16 bitów.
  • #8 5550094
    bajerybajery
    Poziom 10  
    Właśnie niezbyt... Zmienną przes mam zadeklarowaną jako:

     uint32_t przes; 


    (32 bity bez znaku - jest ona dokładnie takiego samego znaku jak inne zmienne (z zwłaszcza te, na których wykonuje przsunięcie))
  • Pomocny post
    #9 5550099
    Freddie Chopin
    Specjalista - Mikrokontrolery
    ja nie znam do konca asm AVRow, ale czy ktorakolwiek ze zmiennych bioracych udzial w tej operacji nie jest zadeklarowana jako signed? (brak definicji signed / unsigned oznacza domyslnie signed)

    
    +0000010E:   FD97        SBRC    R25,7            Skip if bit in register cleared 
    +0000010F:   95A0        COM     R26              One's complement
    


    pozatym rotacja ktora tam widac nie jest do konca prawidlowa, bo operuje tylko na 16bitach (rejestry w AVRach zapewne sa 8bitowe)

    
    +00000109:   0F88        LSL     R24              Logical Shift Left 
    +0000010A:   1F99        ROL     R25              Rotate Left Through Carry
    


    EDIT:
    damn, tym razem ja sie spoznilem [; sprobuj ta linijke zmienic na:
    1ul << przes, bo to faktycznie moze byc zrodlem problemow.

    jesli to nic nie rozwiaze, to dodaj na chwile kolejna zmienna typu uint32, przypisz jej wartosc 1 i sprobuj ja wstawic zamiast owej jedynki.

    4\/3!!
  • #10 5550113
    bajerybajery
    Poziom 10  
    Ha! Mieliście racje! Zapis z L na końcu poskutkował !.

    Dziekuję Wam bardzo: Szelus, Freddie Chopin! Jesteście bardzo mądrzy. Obaj dostajecie "pomógł". Dziękuję!
  • #11 5550128
    szelus
    Poziom 34  
    1ul powinno definitywnie zamkąć sprawę.
    Teraz nie dam głowy, co mówi standard C (i który), tzn, czy dla operatorów << i >> też następuje rozszerzenie typu lewego argumentu do typu prawego argumentu. Jeżeli nie (a z logicznego punktu widzenia nie ma to sensu, bo i tak nie ma jeszcze nawet 128 bitowych typów int), to nie ma tu błedu kompilatora.
  • #12 5550177
    bajerybajery
    Poziom 10  
    Był to jeden z problemów których nie lubię w tym języku ;-) Jeszcze raz dzięki WIELKIE za pomoc.
  • #13 10343243
    cys_ek
    Poziom 10  
    Witam,
    mam podobny problem więc pozwoliłem sobie odkopać temat.

    Problem dotyczy on jednak zmiennej aa. Kiedy chce za jednym razem przesunąć o 8 bitów to wynik jest niepoprawny, jednak jeśli przesunę najpierw o 7 a później o jeden to wynik jest poprawny.

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    kod fragmentu w asm, jednak niewiele on mi mówi
    Kod: text
    Zaloguj się, aby zobaczyć kod
  • #14 10345673
    kriss68
    Poziom 20  
    Pomyśl - zmienna aa ma 8 bitów a Ty jej wartość przesuwasz o 8 bitów w lewo więc ją zerujesz tak na prawdę. Do tego do zmiennej 32bit zapisujesz zmienną 8bit bez rzutowania.
  • #15 10347587
    cys_ek
    Poziom 10  
    Dzięki że wstrząsnąłeś mną, prędzej rzutowałem tylko ze ustawiałem uint8_t zamiast uint32_t.

    dla potomnych, powinno być tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
REKLAMA