LSB <----> MSB tauschen in c

Gast #355913
Lesenswert?

Hallo,

ich muß eine vorhandene Variable x folgendermaßen umschreiben:

x = 0b  MSB    Bit6    Bit5    Bit4    Bit3    Bit2    Bit1    LSB

in

Y = 0b  LSB    Bit1    Bit2    Bit3    Bit4    Bit5    Bit6    Bit7

in asm kein Problem

lsr x
rol y

und das 8 mal -> einfache schnelle Sache.
Aber wie mache ich das schnell und einfach in C?
(mir fallen leider nur komplexe Lösungen ein)

Dank und Gruß
Gast #355916
Lesenswert?

@Marcel
IAR nutze ich leider nicht :(

@Profi
Danke für die Links, aber auch die bieten keinen nutzbaren
Lösungsansatz in C, es sei denn man hat Flashspeicher übrig wie Sand am
Meer.

Der Profi == Profi?

Grüße
#355918
Lesenswert?

http://www.mikrocontroller.net/forum/read-1-304028.html#304197

Elegant und schnell wenn man gut schieben kann:

byte=((byte&0xaa)>>1)|((byte&0x55)<<1);
byte=((byte&0xcc)>>2)|((byte&0x33)<<2);
// Das folgende ist der AVR Befehl 'swap'
byte=((byte&0xf0)>>4)|((byte&0x0f)<<4);

Der AVR schiebt ja so schlecht. Da ist es vermutlich optimal, die
beiden Nibbles des Byte über ne 16ner Tabelle zu spiegeln und dann die
Hälften mit 'swap' zu tauschen.

Cheers
Detlef
Gast #355919
Lesenswert?

Hab hier mal ein Beispielprogramm.
Wenn du eine Schleife daraus machst, wird's kürzer.

int main(void)
{
  unsigned char byte_1,byte_2,temp;

  byte_1 = 0x0f;
  byte_2 = 0;


  {
    temp   |= (byte_1 & 0x80);
    byte_2 |= (temp >> 7);

    temp   |= (byte_1 & 0x40);
    byte_2 |= (temp >> 5);

    temp   |= (byte_1 & 0x20);
    byte_2 |= (temp >> 3);

    temp   |= (byte_1 & 0x10);
    byte_2 |= (temp >> 1);

    temp   |= (byte_1 & 0x08);
    byte_2 |= (temp << 1);

    temp   |= (byte_1 & 0x04);
    byte_2 |= (temp << 3);

    temp   |= (byte_1 & 0x02);
    byte_2 |= (temp << 5);

    temp   |= (byte_1 & 0x01);
    byte_2 |= (temp << 7);

  }

    return 0;
}
Gast #355920
Lesenswert?

Spontan fällt mir dazu ein:
1
unsigned char in = 0x55;
2
unsigned char out = 0;
3

4
char i;
5

6
for(i=0;i<8;i++)
7
    out |= (in & 0x80>>i) ? 1<<i : 0 ;

oder für CPUs ohne barrel shifter:
1
unsigned char in = 0x55;
2
unsigned char out = 0;
3
unsigned char mask = 0x80;
4

5
char i;
6

7
for(i=0;i<8;i++) {
8
    out |= (in & mask) ? 1 : 0;
9
    out<<=1;
10
    mask>>=1;
11
}
Gast #355925
Lesenswert?

Hmm, jetzt habe ich die Optimierung (auf Größe wohlgemerkt) im Compiler
aktiviert, und plötzlich braucht mein Code 50 Bytes und Detlefs 42.

Aber ich habe festgestellt, daß mein Code unnötig umständlich war,
diese Version benötigt nur 29 Bytes bei aktivierter Optimierung:
1
char i,out;
2

3
for(i=0;i<8;i++) {
4
    out >>= 1;
5
    out |= (byte & 0x80);
6
    byte <<= 1;
7
}
#355926
Lesenswert?

Hallo Marko,

Du hast die Initialisierung von 'out' nicht drin. Der Code sollte
schnell sein, hatte ich verstanden. Hast Du mal die benötigten
Prozessorcycles für die beiden Versionen ausgezählt, das wäre
interessant!?

@seacrash: Wer lesen kann ist klar im Vorteil!

Cheers
Detlef
Gast #355929
Lesenswert?

Hi Detlef,

hast Recht. Das kam daher, daß ich den Code in GCC teste, aber in NC30
kompiliere um den Platzbedarf auf dem Controller zu bestimmen.
Witzigerweise braucht meine Funktion mit der Initialisierung nur 25
Byte.

Hier der genaue Code, den ich verglichen habe:
1
unsigned char mirror(unsigned char byte)
2
{
3
    char out = 0, i;
4

5
    for(i=0;i<8;i++) {
6
        out >>= 1;
7
        out |= (byte & 0x80);
8
        byte <<= 1;
9
    }
10
    return out;
11
}

und Deine Version:
1
unsigned char mirror(unsigned char byte)
2
{
3
    byte=((byte&0xaa)>>1)|((byte&0x55)<<1);
4
    byte=((byte&0xcc)>>2)|((byte&0x33)<<2);
5
    byte=((byte&0xf0)>>4)|((byte&0x0f)<<4); 
6

7
    return byte;
8
}

Ich habe die Taktzyklen nicht genau gezählt, schätze aber, daß deine
Version ca. um den Faktor 2-3 schneller ist (Listing ist im Anhang).
Das aber nur, weil der R8C einen Barrel Shifter hat. Auf einem AVR
dürfte meine Version gleichauf oder sogar schneller sein. Vielleicht
kann ja mal jemand den Code für AVR compilieren und vergleichen, ich
hab gerade keinen Compiler zur Hand.

Läubi: logisch, in Assembler geht das viel einfacher und schneller, da
kann man einfach durchs Carry shiften. Ist dann aber nicht portabel.
Angehängte Dateien:
#355930
Lesenswert?

Hallo Marko,

ja, sowas Schönes wie barrel-shifter hat der AVR nicht. Die letzte
C-Zeile von mir ist allerdings auf dem AVR ein einziger Befehl, das
erwähnte 'swap'. Für den AVR sollte das mit swap und einer Tabelle
aus 8  oder 16 chars schneller geht: Halbbyte ist zweimal die Adresse
in die Tabelle, 16 Einträge zu ja halbem Byte macht 8 Byte. Naja, ist
was für nen Novembersonntag.

Cheers
Detlef
Gast #355931
Lesenswert?

@seacrash:
Wenn das noch nicht genug "profimassig" war, wie wärs dann mit:
bit=0x01;out=0;
for (mask=0x80;mask;mask/=2){
  if (x&mask){y|=bit;}
  bit*=2;}

Oder ohne Schleife:
  y=x&128?1:0 | x&64?2:0 | x&32?4:0 | x&16?8:0 |
    x& 8?16:0 | x&4?32:0 | x&2?64:0 | x&1?128:0;
Inwieweit der Compiler das elegant löst??

Oder brute force:
  if(x&128){y=1;}else{y=0;}
  if(x& 64){y|=  2;}
  if(x& 32){y|=  4;}
  if(x& 16){y|=  8;}
  if(x&  8){y|= 16;}
  if(x&  4){y|= 32;}
  if(x&  2){y|= 64;}
  if(x&  1){y|=128;}


Außerdem:
for(h=1; h ; h<<1){    dürfte nicht die gewünschte Wirkung haben...

for(h=1; h ; h<<=1){   oder
for(h=1; h ; h*=2){
Gast #355932
Lesenswert?

Profi: alles etwas umständlich, aber es hat mich auf eine Idee gebracht,
man braucht die Zählvariable ja gar nicht:
1
unsigned char mirror(unsigned char byte)
2
{
3
  char out = 0;
4

5
  for(;byte;byte<<=1) {
6
    out >>= 1;
7
    out |= (byte & 0x80);
8
  }
9

10
  return out;
11
}

Nur noch 21 Bytes. =)
Gast #355934
Lesenswert?

in meiner 3. Zeile muss es statt out=0 natürlich y=0 heißen.

oder funktioniert's doch, denn bei x=0 kann die Schleife beendet
werden, falls das Ergebnis y nicht geschoben werden muss:

  j=128;y=0;
  do{
    if(x&1){y|=j;}
    j/=2;}
  while(x/=2);    //kann eine Warnung geben, ist so beabsichtigt

oder als for-Schleife geschrieben:
  for(j=128,y=0;x;x/=2){
    if(x&1){y|=j;}
    j/=2;}

evtl. kann es günstiger sein, andersherum zu schieben (falls die
Schleife wg. der Bitmuster so schneller beendet wird):
  for(j=1,y=0;x;x*=2){
    if(x&128){y|=j;}
    j*=2;}

Naj, gut für heute mit dem Geschiebe ;-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren