Hallo!! Ich habe ein ganz einfaches Problem: Ein Zähler soll Werte zwischen 0 und 255 annehmen können. Also eine 8 Bit Variable. Natürlich kann diese einfach in-/dekrementiert werden. Aber gibt es einen Trick effizient den Über- bzw. Unterlauf zu verhindern? Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler Code... Vielleicht eine arithmetische Lösung? Viele Grüße, Klaus
Warum nimmst du nicht einfach eine 16-Bit-Variable, die in- und dekrementiert wird? Wenn dann das High-Byte ungleich null ist, gab es einen Überlauf. Subtraktion des High-Bytes vom Lowbyte, ohne if-Abfrage, müsste dann das leisten, was du willst.
Was willst du denn tun? soll durch +1 rechnen die variable bei 255 stehen bleiben? icrgendwei versteh ich dich nicht
Matthias Lipinsky wrote: > Was willst du denn tun? > soll durch +1 rechnen die variable bei 255 stehen bleiben? > > icrgendwei versteh ich dich nicht Ja, genau. In Assembler würde ich halt einfach das Carry-Flag wegwerfen...
>In Assembler würde ich halt einfach das Carry-Flag wegwerfen... Das stimmt nicht. Wenn du das Carrayflag wegwirst, dann schmeist du die Info weg, dass er übergelaufen ist.. >Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler >Code... Poste den vielen Code doch mal!
@texmex: Was soll das den bringen? Dann folgt auf 255 die 0. Wenn du bei 255 stehen bleiben willst, dann musst du nach add +1 das C Flag wieder abziehen.
So übel finde ich den Code übrigens nicht. Aus if (++i == 0) i = 255; wird subi r17,lo8(-(1)) brne .L2 ldi r17,lo8(-1) .L2: Ok, ein bischen unsauber ist der Quellcode schon, denn er setzt zwingend voraus, dass die Variable als 8bit Byte implementiert ist und nicht etwa in 9 Bits. Ist heutzutage aber recht verbreitet.
Matthias Lipinsky wrote: >>In Assembler würde ich halt einfach das Carry-Flag wegwerfen... > Das stimmt nicht. Wenn du das Carrayflag wegwirst, dann schmeist du die > Info weg, dass er übergelaufen ist.. > > >>Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler >>Code... > Poste den vielen Code doch mal! Naja, beim Dekrementieren z.B. so:
1 | if ( dcf77_accuracy ) dcf77_accuracy--; |
2 | 234a: 80 91 b5 01 lds r24, 0x01B5 |
3 | 234e: 88 23 and r24, r24 |
4 | 2350: 29 f0 breq .+10 ; 0x235c <__vector_3+0x$ |
5 | 2352: 80 91 b5 01 lds r24, 0x01B5 |
6 | 2356: 81 50 subi r24, 0x01 ; 1 |
7 | 2358: 80 93 b5 01 sts 0x01B5, r24 |
Aber wenn ich mir das gerade so ansehe, liegt das wohl eher daran, dass die Variable erst direkt aus dem Ram geladen wird und nicht in einem Register steht!?!?
@lippy: Doch: ldi r0,1 add r,r0 sbci r,0 ist zwar nicht kürzer, aber schneller. @texmex: Speicher ist eines, aber dann auch noch volatile...
A. K. wrote: > @texmex: Was soll das den bringen? Dann folgt auf 255 die 0. Wenn du bei > 255 stehen bleiben willst, dann musst du nach add +1 das C Flag wieder > abziehen. Hm, stimmt.
>Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler >Code... In Assembler selbst sind beide Teilaufgaben mit je vier Instruktionen erledigt:
1 | LimitedIncrement: |
2 | cpi t, MAXVALUE |
3 | breq LimitedIncrementEnd |
4 | inc t |
5 | LimitedIncrementEnd: |
6 | ret |
7 | |
8 | |
9 | LimitedDecrement: |
10 | cpi t, MINVALUE |
11 | breq LimitedDecrementEnd |
12 | dec t |
13 | LimitedDecrementEnd: |
14 | ret |
Wenn Du es noch kompakter haben willst und zwei Register freizustellen bereit bist, kannst Du auch die cpse-Instruktion verwenden:
1 | ; (Register min und max irgendwo im Initialisierungsteil |
2 | ; des Programms mit MINVALUE resp. MAXVALUE laden) |
3 | |
4 | LimitedIncrement: |
5 | cpse t, max |
6 | inc t |
7 | LimitedIncrementEnd: |
8 | ret |
9 | |
10 | |
11 | LimitedDecrement: |
12 | cpse t, min |
13 | dec t |
14 | LimitedDecrementEnd: |
15 | ret |
A. K. wrote: > @lippy: Doch: > ldi r0,1 > add r,r0 > sbci r,0 > ist zwar nicht kürzer, aber schneller. > > @texmex: Speicher ist eines, aber dann auch noch volatile... Ah, verstehe! Deshalb wird das dann anschließend gleich nochmal aus dem Ram geladen. Wäre es denn evtl. sinnvoll die volatile Variable am Anfang der Interrupt Routine in eine lokale Variable zu kopieren (die dann vermutlich in einem Register landet), wenn sie häufiger modifiziert wird? Oder macht man "sowas" ganz anders? viele Grüße, Klaus
Oh, das ist ja das GCC-Forum hier (ups, erst jetzt gesehn). Sollte Dich Assemblercode nicht interessieren: Sorry!
Klaus W. wrote:
> Oder macht man "sowas" ganz anders?
Nö, genau so. Standardproblem von volatile.
Gast wrote: > Oh, das ist ja das GCC-Forum hier (ups, erst jetzt gesehn). Sollte Dich > Assemblercode nicht interessieren: Sorry! Nun, das eine schliesst das andere ja nicht aus :-).
A. K. wrote: > Klaus W. wrote: > >> Oder macht man "sowas" ganz anders? > > Nö, genau so. Standardproblem von volatile. Mir kam da grade die Speicherklasse "register" in den Sinn. Tatsächlich findet sich in http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_regbind ein entsprechender Hinweis. Aber das bietet wohl auch so allerlei Fallstricke....
> Ich habe ein ganz einfaches Problem: No Problem for me ! > Ein Zähler soll Werte zwischen 0 und 255 annehmen können. Also eine 8 > Bit Variable. All right. > Natürlich kann diese einfach in-/dekrementiert werden. Lets go. > Aber gibt es einen Trick effizient den Über- bzw. Unterlauf zu > verhindern? Yes, sir ! > Jedesmal mit if zu prüfen produziert so schrecklich viel Assembler > Code... Yeah, thats true. > Vielleicht eine arithmetische Lösung? CPU boardmittel ! Also, hier die ultimative Lösung: Du programmierst einen Timer und setzt das Kompareflag ! Dann Vergleicht die CPU und erzeugt einen Interrupt, unhabhängig vom Programm und alles ist bestens ! 100% Effe ;-)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.