hallo alle!
ich möchte die reihenfolge der bits in einem byte mit C umdrehen. also
zb aus
10001001 -> 10010001 machen. muss ich das in einer schleife tun oder
gibt es dafür eine funktion.
Das musst Du schon selbst erledigen. Eine Funktion gibt es dafür nicht.
Kommt es auf Geschwindigkeit an, dann ließe sich eine Tabelle mit 256
Einträgen verwenden ...
Und, hast du mal nachgerechnet, wie elegant das wirklich ist?
Das letzte Mal, als ich es nachgerechnet habe, war die simple Schleife
bei einem Byte, das es umzudrehen gilt, kürzer und schneller.
Hängt natürlich sehr davon ab, wie "smart" der Optimizer ist. Im
Idealfall wird für "Val = (Val&0xF0)>>4 | (Val&0x0F)<<4;" eine "swap
Val" ASM-Instruktion ausgeführt!
Habs mal ausprobiert und dabei mit Ensetzen festgestellt, dass der
aktuelle WinAVR mit -OS ein riesen Schrott erzeugt!
1
unsignedcharVal;
2
Val=PORTC;
3
Val=(Val&0xF0)>>4|(Val&0x0F)<<4;
4
Val=(Val&0xCC)>>2|(Val&0x33)<<2;
5
Val=(Val&0xAA)>>1|(Val&0x55)<<1;
6
PORTC=Val;
Wird zu:
1
Val=PORTC;
2
INR18,0x15InfromI/Olocation
3
Val=(Val&0xF0)>>4|(Val&0x0F)<<4;
4
CLRR19ClearRegister
5
Val=(Val&0xCC)>>2|(Val&0x33)<<2;
6
MOVR24,R18Copyregister
7
SWAPR24Swapnibbles
8
ANDIR24,0xF0LogicalANDwithimmediate
9
LDIR23,0x04Loadimmediate
10
LSRR19Logicalshiftright
11
RORR18Rotaterightthroughcarry
12
DECR23Decrement
13
BRNEPC-0x03Branchifnotequal
14
ORR24,R18LogicalOR
15
CLRR25ClearRegister
16
63:Val=(Val&0xAA)>>1|(Val&0x55)<<1;
17
MOVWR18,R24Copyregisterpair
18
ANDIR18,0xCCLogicalANDwithimmediate
19
ANDIR19,0x00LogicalANDwithimmediate
20
ASRR19Arithmeticshiftright
21
RORR18Rotaterightthroughcarry
22
ASRR19Arithmeticshiftright
23
RORR18Rotaterightthroughcarry
24
ANDIR24,0x33LogicalANDwithimmediate
25
ANDIR25,0x00LogicalANDwithimmediate
26
LSLR24LogicalShiftLeft
27
ROLR25RotateLeftThroughCarry
28
LSLR24LogicalShiftLeft
29
ROLR25RotateLeftThroughCarry
30
ORR18,R24LogicalOR
31
MOVR24,R18Copyregister
32
CLRR25ClearRegister
33
MOVWR18,R24Copyregisterpair
34
ANDIR18,0xAALogicalANDwithimmediate
35
ANDIR19,0x00LogicalANDwithimmediate
36
ASRR19Arithmeticshiftright
37
RORR18Rotaterightthroughcarry
38
ANDIR24,0x55LogicalANDwithimmediate
39
ANDIR25,0x00LogicalANDwithimmediate
40
LSLR24LogicalShiftLeft
41
ROLR25RotateLeftThroughCarry
42
ORR18,R24LogicalOR
43
PORTC=Val;
44
OUT0x15,R18OuttoI/Olocation
Ist ja echt zum kotzen, hab eigentlich immer gedacht der AVR-GCC sei
besser!
Peter
Abgesehen vom bekannten Problem der überflüssigen Bearbeitung der
high bytes, was bitte würdest du an diesem Code noch großartig
verbessern wollen?
Du hast vergessen, dass der "smart" aussehene C-Code auf einer
Maschine, die nicht shift/rotate N kann, sondern nur shift/rotate 1
zwangsläufig einen ganzen Batzen Code erzeugen muss.
Naja, ich würde erwarten, dass der Otimizer tut, was er tun sollte,
nämlich den generierten Code optimeren. Ich finde dazu dazu gehört auch
dass er erkennen müsste, wenn int Konstanten auf 1 Byte reduziert werden
dürfen, weil alle anderen Terme des Ausdrucks ebenfalls nur 8-Bit Werte
sind.
Das mindeste wäre, dass wenn ich schon die Konstanten explizit auf
unsigned char caste, keine 16 Bit Operationen mehr erzeugt werden.
Wieso werden Typecasts von Konstanten ignoriert?
Ich würde in etwa den folgenden Output erwarten:
Peter wrote:
> Ich finde dazu dazu gehört auch dass er erkennen müsste, wenn int> Konstanten auf 1 Byte reduziert werden dürfen, weil alle anderen> Terme des Ausdrucks ebenfalls nur 8-Bit Werte sind.
Dass er das oft nicht (richtig gut) kann, ist ein alter Hut. Das
liegt einerseits daran, dass das bei GCC sonst für kaum ein anderes
Target wirklich notwendig ist (weil deren natürliche Wortbreite den
Mindestforderungen an ein int gemäß C-Standard entspricht), zweitens
liegt es wohl (so hat mir mal jemand gesagt) daran, dass der AVR-GCC
nicht als richtige 8-bit-Maschine implementiert ist, sondern als
fiktive 16-bit-Maschine, bei der dann Ausnahmen für 8 bits separat
definiert worden sind.
> Das mindeste wäre, dass wenn ich schon die Konstanten explizit auf> unsigned char caste, keine 16 Bit Operationen mehr erzeugt werden.
Du kannst die Konstante casten wie du willst, der C-Standard verlangt,
dass die Rechnung so auszuführen ist, als wären alle
Zwischenergebnisse wenigstens vom Typ "int". Genau das macht der
Compiler dann folglich erst einmal (damit die compliance
sichergestellt ist), und beim anschließenden Erkennen, dass das obere
Byte nichts zum Ergebnis beitragen kann, fehlt's zuweilen.
Davon abgesehen funktioniert es mit Peters Version schon recht gut.
Dort fehlt eigentlich fast nur noch die Erkenntniss, dass
1
mov r25,r24
2
swap r25
3
andi r25,0xf0
4
swap r24
5
andi r24,0x0f
6
or r24,r25
7
mov r25,r24
nichts weiter als
1
swap r25
2
mov r24,r25
macht. Das müsste man wohl dem Optimizer wirklich mal beibringen
können, gibt's hier jemanden, der genügend RTL versteht das zu tun?
;-)
Dennoch, wenn du's insgesamt nachrechnest, selbst mit dem reparierten
swap ist das Ergebnis nicht wirklich besser (gleich schnell mit mehr
Code) als mit einer einfachen Schleife. So genial dieser Algorithmus
für große Zahlen ist, bei 8 bits kann er seinen Charme nicht
ausspielen.
Peter wrote:
> Okydoky, der Loop ist dann offenbar tatsächlich die beste Lösung, wenn> man nicht etwas in Assembler stricken möchte!
Du hast es immer noch nicht verstanden: selbst in Assembler wirst du
nicht besser werden als mit der Schleife.
<WAS> ist schon besser?
Wenn das selten gemacht wird, dann "besser" ggf. wenig Speicherbedarf
haben, die Laufzeit ist nicht so wichtig.
Muss das sehr oft getan werden, ist es genau andersherum.
Im Extremfall lohnt es sich, eine Tabelle mit den gespiegelten Bitwerten
anzulegen und darauf zugreifen:
Stefan Kleinwort wrote:
> Wenn das selten gemacht wird, dann "besser" ggf. wenig Speicherbedarf> haben, die Laufzeit ist nicht so wichtig.
Es ging ja darum, dass die mit der Hand entrollte und in Assembler
codierte Form dieses lustigen Algorithmus weder schneller noch
kürzer als die einfache Schleife ist, wenn man nur 8 bits hat.
> Im Extremfall lohnt es sich, eine Tabelle mit den gespiegelten Bitwerten> anzulegen und darauf zugreifen:
Ja, das ist unbestritten die schnellste Variante.
@ Rufus
0x00, 0x80, 0x40, 0x20, ...};
Auch diese Reihenfolge überzeugt mich noch nicht so recht, machen wir
doch mal ein Bit reversing zur Probe (alles in Hex):
00 -> 00 ganz klar
80 -> 01 auch gut
40 -> 02 passt
20 -> 04 ähhhh..
Da gehört vielleicht noch C0 zwischen 40 und 20.
Ein Kompromiss zwischen Codegröße und Geschwindigkeit wäre es, nur 16
Nibbles abzulegen und aus der Tabelle zu holen.
So habe ich das bei meinen Bytes gemacht und es müsste auch für 16 und
32 Bit Zahlen funktionieren.
PS: ich weiß dass der thread älter ist, aber es war der erste Treffer in
meiner Suche und beinhaltet viele Lösungsvorschläge, da dachte ich
sollte dieser nicht fehlen :)
Daniel Polz wrote:
> Register Stunden "spiegeln".
Stunden? Nein, Jahre. Später als die letzte vorhandene Antwort...
> fertig... läuft... ganz ohne tabellen
Aber halt auch mit 17 Takten. Ein Tabellenzugriff in eine ROM-
Tabelle geht schneller.
Jörg Wunsch wrote:
> Aber halt auch mit 17 Takten. Ein Tabellenzugriff in eine ROM-> Tabelle geht schneller.
Wirklich? Ist der Flash tatsaechlich so schnell? Eine Lookup-Tabelle
muss ja auch nur 254 Bytes gross sein, wenn man die Operation haeufig
braucht koennte es sich auch lohnen, sie im SRAM zu halten.
@ Michael G. (linuxgeek) Benutzerseite
>Wirklich? Ist der Flash tatsaechlich so schnell?
RTFM. Der AVR braucht für einen Flashzugriff 3 Takte, für den SRAM 2.
>braucht koennte es sich auch lohnen, sie im SRAM zu halten.
Geringfügig.
MFG
Falk
dö schrieb:
> Peter Dannegger schrieb:>>> Versuch mal das hier:>> unsigned char mirror( unsigned char a )>> {>> a = ((a >> 4) & 0x0F) | ((a << 4) & 0xF0);>> a = ((a >> 2) & 0x33) | ((a << 2) & 0xCC);>> a = ((a >> 1) & 0x55) | ((a << 1) & 0xAA);>>>> return a;>> }>>>>>> Peter>>> gehe ich richtig der annahme, dass wenn ich das ganze für 16 bit machen> will, ich nur noch eine zeile mit 8 einfügen muss und die Vergleiche> entsprechend ergänzen??
Warum probierst du es nicht einfach aus?
Würde ich so machen: