PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Bits in einem Byte vertauschen



KingTobi
07.12.2008, 17:19
Hi

Ich habe ein Byte, in dem ich Bit 2 und 4 miteinander austauschen möchte.
Wie kann ich das am "elegantesten" tun?

gruß

error41
07.12.2008, 18:12
Hi!
Ich habs mal ausm Ärmel geschüttelt. (Also nicht getestet)
So müsste es gehen:


bla = (bla & 0xF5) | ((bla & 0x02)<<2) | ((bla & 0x08)>>2);

Ich ging mal davon aus dass du mit Bit 2 das Bit an zweiter stelle von rechts meintest. (LSB ist Bit1)
Wenn nicht, dann probiers damit: (LSB ist Bit0)


bla = (bla & 0xE6) | ((bla & 0x04)<<2) | ((bla & 0x10)>>2);


Ob das nun eine "elegante" Methode ist, darüber lässt sich sicher streiten.
Ich würds so machen... :-)

Gruß

fhs
07.12.2008, 18:15
Hi,

ganz stur so (Bitzählung von 0 bis 7!):



void inline bitswp2_4(uint8_t b) {
asm volatile (
"bst %0,2" "\n\t"
"sbr %0,4" "\n\t"
"sbrs %0,4" "\n\t"
"cbr %0,4" "\n\t"
"bld %0,4" "\n\t"
:
: "d" (b)
);
}



Schnell hingeschrieben und ungetestet. Mal sehen, ob jemand eine kürzere Lösung findet! Anmerkung: "inttypes.h" muss eingebunden sein. Aufruf mit
"bitswp2_4(byte_argument)".

Gruß

Fred

fhs
07.12.2008, 18:24
Hier noch eine Zeile kürzer:


void inline bitswp2_4_new(uint8_t b) {
asm volatile (
"mov __tmp_reg__, %0" "\n\t"
"bst %0,2" "\n\t"
"bld %0,4" "\n\t"
"bst __tmp_reg__,4" "\n\t"
"bld %0,2" "\n\t"
:
: "d" (b)
);
}

thewulf00
07.12.2008, 18:43
Also fhs. Deine Posts begeistern mich immer wieder. Starkes Stück Code!

fhs
07.12.2008, 18:48
Danke für die Blumen ... ich hoffe, der Code stimmt! Eigentlich stehe ich mit dem Inline-Assembler auf Kriegsfuß (bin sonst ein sehr friedlicher Typ), aber ab und zu ist der dann doch hilfreich. Überlege gerade, ob man's nicht noch kürzer machen könnte.

fhs
07.12.2008, 19:18
Und hier noch mal in C:


uint8_t bitswp2_4_in_c(uint8_t byte) {
uint8_t anded=byte&&#40;uint8_t)0x14;
if (anded==(uint8_t)0x14 || !anded) return byte; // bits identisch, nix ändern!
return byte^0x14; // bits invertieren
}


Mit Optimierung "-O1" sieht das so kurz aus:


47: uint8_t bitswp2_4_in_c(uint8_t byte) {
+000000DD: 2F98 MOV R25,R24 Copy register
48: uint8_t anded=byte&0x14;
+000000DE: 7184 ANDI R24,0x14 Logical AND with immediate
49: if (anded==(uint8_t)0x14 || !anded) return byte;
+000000DF: 3184 CPI R24,0x14 Compare with immediate
+000000E0: F021 BREQ PC+0x05 Branch if equal
+000000E1: 2388 TST R24 Test for Zero or Minus
+000000E2: F011 BREQ PC+0x03 Branch if equal
50: return byte^0x14;
+000000E3: E184 LDI R24,0x14 Load immediate
+000000E4: 2798 EOR R25,R24 Exclusive OR
51: }
+000000E5: 2F89 MOV R24,R25 Copy register
+000000E6: 9508 RET


Eleganter kann ich's nicht.

sternst
07.12.2008, 22:02
Also fhs. Deine Posts begeistern mich immer wieder. Starkes Stück Code!

Ja, guter Ansatz. Nur schade, dass das mühsam errechnete Ergebnis so achtlos weggeworfen wird. ;-)

Besser:

static inline void bitswp2_4(uint8_t *b) {
asm volatile (
"mov __tmp_reg__, %0" "\n\t"
"bst %0,2" "\n\t"
"bld %0,4" "\n\t"
"bst __tmp_reg__,4" "\n\t"
"bld %0,2" "\n\t"
: "=d" (*b)
: "0" (*b)
);
}

fhs
08.12.2008, 13:39
Hallo Stefan,

oops, ja, natürlich -- danke für die Verbesserung!

Gruß

Fred

KingTobi
08.12.2008, 14:14
Hi

Danke für die schnelle und ausführliche Hilfe!
Hab es jetzt aber doch per Hardware gelöst.

SprinterSB
10.12.2008, 23:14
hmmm...

was heisst denn elegent?

Elegant und korrekt als Inline Asm:

static inline unsigned char
swap_2_4_asm (unsigned char a)
{
unsigned char b;

asm volatile ("bst %[a],2" "\n\t"
"bld %[b],4" "\n\t"
"bst %[a],4" "\n\t"
"bld %[b],2"
: [b] "=&r" (b) : [a] "r" (a), "0" (a));

return b;
}

uint8_t foo_asm (uint8_t b)
{
return swap_2_4_asm (b);
}


Bei Inline-Assembler ist unbedingt darauf zu achten, daß der Effekt auf die Maschine dem Compiler korrekt mitgeteilt wird!

Das Ergebnis nur an einem Beispiel testen genügt nicht. Wenn sich das Programm ändert und der Compiler etwas anderen Code erzeugt, fliegen einem die obigen asm-Schnippsel um die Ohren (bzw. der AVR macht es).

In C kann man es elegant über Bitfelder lösen, was leider von gcc momentan noch -- gelinde gesagt -- suboptimal übersetzt wird:


typedef union
{
struct
{
unsigned b0 :1;
unsigned b1 :1;
unsigned b2 :1;
unsigned b3 :1;
unsigned b4 :1;
unsigned b5 :1;
unsigned b6 :1;
unsigned b7 :1;
};

unsigned char val;
} bit8_t;

static inline unsigned char
swap_2_4_c (unsigned char a)
{
bit8_t xa = {.val = a};
bit8_t xb = {.val = a};

xb.b4 = xa.b2;
xb.b2 = xa.b4;

return xb.val;
}

uint8_t foo_c (uint8_t b)
{
return swap_2_4_c (b);
}

sternst
11.12.2008, 08:13
fliegen einem die obigen asm-Schnippsel um die Ohren
Kannst du bitte mal erläutern, inwieweit das meinen "Schnipsel" betrifft?

SprinterSB
11.12.2008, 12:38
Kannst du bitte mal erläutern, inwieweit das meinen "Schnipsel" betrifft?

Dessen Beschreibung ist korrekt, allerdings sollte folgendes badacht werden:

-- Das Register muss nicht in der Klasse "d" liegen, Klasse "r" genügt vollauf. Falls das Register zB in "l" lebt, würden sonst überflüssige Reloads nach "d" fällig.

-- Wenn man das auf eine lokale Variable anwendet, ist der Code, den gcc zum Erzeugen der Adresse macht, oft mehr als der eigentliche Code des asm. Daher sollte wenn möglich Adressbildungen lokaler Variablen vermieden werden, insbesondere wenn man dichten, flotten Code haben möchte:


char c;
char foo (char b)
{
char a = b;
bitswp2_4 (&a);

c = a;

return b;
}


Das hab ich mal mit avr-gcc 4.4.0 übersetzt; ältere gcc-Versionen machen nicht unbedingt besser...


foo:
push r29
push r28
push __tmp_reg__
in r28,__SP_L__
in r29,__SP_H__
/* prologue: function */
/* frame size = 1 */
mov r25,r24
/* #APP */
; 7 "swap.c" 1
mov __tmp_reg__, r25
bst r25,2
bld r25,4
bst __tmp_reg__,4
bld r25,2

; 0 "" 2
/* #NOAPP */
sts c,r25
/* epilogue start */
pop __tmp_reg__
pop r28
pop r29
ret


Um die Adresse einer lokalen Variablen nehmen zu können, muss diese im Frame (bzw. im Speicher, hier aufm Stack) angelegt werden. Von Maschinenregistern kann nämlich keine Adresse genommen werden.

Teilweise gelingt es GCC, das wieder halbwegs optimal hinzubiegen, aber welch harte Nuss das ist, wird erst klar, wenn man ein bissl unter die Haube eines Compilers guckt...

sternst
11.12.2008, 13:39
Das ist mir alles bekannt. Ich habe auch nie den Anspruch auf "optimale Lösung" erhoben, schließlich habe ich nur Freds Variante korrigiert.
Ich wollte nur wissen, in welcher Hinsicht mir mein Code "um die Ohren fliegt".