Forum: Mikrocontroller und Digitale Elektronik Bits shiften - ich verstehe es nicht.


von NegativeOne! (Gast)


Lesenswert?

Hallo Leute,

ich bin überfordert...

Kurzbeschreibung:
Über drei BCD Codierschalter soll eine Dezimalzahl eingestellt 
werden...0-999.

Diese drei Codierschalter hängen an zwei Schieberegistern, allerdings 
nicht so, dass ich die Bits der Reihe nach in eine 16 Bit Variable 
shiften könnte. Die Stellen der drei Schalter sind nämlich vertauscht.

Ich muss daher die bits der beiden, aus dem Schieberegister gelesenen 
Bytes, neu zusammensetzen und dabei auf drei 8-bit Variablen verteilen.

Und dazu auch noch negiert werden, denn was in den Ergebnis-Bytes eine 1 
sein muss, kommt als 0 aus dem Schieberegister.

Also zum Beispiel:
1
uint8_t hunderter = 0;
2
uint8_t zehner = 0;
3
uint8_t einer = 0;
4
5
firstbyte  = readBCDSwitches();
6
secondbyte = readBCDSwitches();
7
8
  // Hunderter: 
9
  // 00000000
10
  //     ||||
11
  //     |||+--- !firstbyte.0
12
  //     ||+---- !firstbyte.2
13
  //     |+----- !firstbyte.1
14
  //     +------ !firstbyte.3
15
  
16
  // Zehner:
17
  // 00000000
18
  //     ||||
19
  //     |||+--- !secondbyte.4
20
  //     ||+---- !secondbyte.6
21
  //     |+----- !secondbyte.5
22
  //     +------ !secondbyte.7
23
24
  // Einer:
25
  // 00000000
26
  //     ||||
27
  //     |||+--- !secondbyte.0
28
  //     ||+---- !secondbyte.2
29
  //     |+----- !secondbyte.1
30
  //     +------ !secondbyte.3
31
32
hunderter = (..????..);

Kann mir vielleicht jemand ein Beispiel geben, wie man das Bit 2 aus der 
Quellvariable negiert als Bit 1 in die Zielvariable schreibt?

Ich checks einfach nicht...

Vielen Dank für jeden Tip.

von Jens M. (schuchkleisser)


Lesenswert?

Ziel = 0;
if Quelle.bit=0 then ziel.bit=1;
und dann einfach 11 weitere ifs.

In Assembler sind das je nach Prozessor so ~27 Befehle (3 clr, 12x bit 
test und bit set).
In C etwas mehr zu schreiben, aber ebenfalls trivial.

Wie man Bits abfragt und setzt weißt du aber?

: Bearbeitet durch User
von Christoph db1uq K. (christoph_kessler)


Lesenswert?

negieren geht durch ein VerUNDen mit 0xffff

Und wenn die Zehner nur in der falschen Richtung laufen, kann man sie 
z.B. über das Überlaufflag in eine andere Variable in umgekehrter 
Richtung reinschieben. Aber hier sind die vier Bits ja noch 
komplizierter angeordnet.

: Bearbeitet durch User
von H.Joachim S. (crazyhorse)


Lesenswert?

Am einfachsten mit einer Tabelle aus 16 Werten, also zu deiner Belegung 
steht die jeweilig richtige Zahl. Dazu noch ein bisschen maskieren und 
bei secondbyte 4xschieben.

von Jens M. (schuchkleisser)


Lesenswert?

Christoph db1uq K. schrieb:
> negieren geht durch ein VerUNDen mit 0xffff

Seit wann? Wenn dann XOR. und ein Byte XORt man mit xFF.
Aber nötig ist das negieren hier nicht.

H.Joachim S. schrieb:
> Am einfachsten mit einer Tabelle aus 16 Werten, also zu deiner Belegung
> steht die jeweilig richtige Zahl. Dazu noch ein bisschen maskieren und
> bei secondbyte 4xschieben.

Oh ja, eine LUT. Noch mehr Speicher und noch komplizierter. Und dann 
noch maskieren und schieben.

: Bearbeitet durch User
von Bitshifter (Gast)


Lesenswert?

oder mal nachdenken, ob es so gehen kann :-) :

1. Bit 1 in der Zielvariablen löschen
2. Inhalt der Quellvariablen so shiften, dass Bit 2 anschließend an der 
Stelle Bit 1 steht.
3. alle Bits ausser Bit 1 löschen (inverse Maske von 1.)
4. mit der Zielvariablen "verodern"

von Bitshifter (Gast)


Lesenswert?

ach ja: vor Schritt 3 noch negieren

von Jens M. (schuchkleisser)


Lesenswert?

So etwa:
1
int main()
2
{
3
    unsigned char foo=0x03;
4
    unsigned char bar=0;
5
    
6
    printf("Input: %x\n", foo);
7
8
    if (foo & 0x01) bar += 0x10;
9
    if (foo & 0x02) bar += 0x20;
10
    if (foo & 0x04) bar += 0x40;
11
    if (foo & 0x08) bar += 0x80;
12
    
13
    printf("Output: %x\n", bar);
14
15
    return 0;
16
}

von Marten Morten (Gast)


Lesenswert?

NegativeOne! schrieb:
> Also zum Beispiel:
>
>
1
> firstbyte  = readBCDSwitches();
2
> secondbyte = readBCDSwitches();
3
> 
4
...
5
>   // Zehner:
6
>   // 00000000
7
>   //     ||||
8
>   //     |||+--- !secondbyte.4
9
>   //     ||+---- !secondbyte.6
10
>   //     |+----- !secondbyte.5
11
>   //     +------ !secondbyte.7
12
> 
13
>   // Einer:
14
>   // 00000000
15
>   //     ||||
16
>   //     |||+--- !secondbyte.0
17
>   //     ||+---- !secondbyte.2
18
>   //     |+----- !secondbyte.1
19
>   //     +------ !secondbyte.3
20
> 
21
>

Wenn du wirklich nur zwei Bytes bekommst, dann können Zehner und Einer 
so nicht im zweiten Byte kodiert sein.

Ansonsten sind immer nur die mittleren Bits (2 <-> 1, bzw. 6 <-> 5) 
vertauscht. Ausmaskieren und zurück tauchen:
1
unit8_t swap21(uint8_t in) {
2
    return (in & !0x06) | ((in & 0x02) << 1) | (in & 0x04) >> 1) 
3
}
4
5
unit8_t swap65(uint8_t in) {
6
    return (in & !0x60) | ((in & 0x20) << 1) | (in & 0x40) >> 1) 
7
}

Negieren kann man vorher oder nachher. firstbyte ist nach dem swap21() 
direkt das hunderter Byte. secondbyte muss nach swap21() und swap(65) 
noch auseinander "geshiftet" werden. Wie genau hängt davon ab wo sich 
Zehner und Einer wirklich in secondbyte befinden.

von Wolfgang (Gast)


Lesenswert?

NegativeOne! schrieb:
> Diese drei Codierschalter hängen an zwei Schieberegistern, allerdings
> nicht so, dass ich die Bits der Reihe nach in eine 16 Bit Variable
> shiften könnte. Die Stellen der drei Schalter sind nämlich vertauscht.

Da wurden beim Routen der Platine ein paar Minuten eingespart, die 
Entwicklungsarbeit wurde verkürzt. Die Software muss es jetzt ausbaden. 
;-(

Wenigstens scheinen die Gebersignale alle in gleicher Weise verknotet zu 
sein, so dass du mit einem über das Gebersignal indizierten Zugriff auf 
ein 16er-Array die Sache ohne große Bitschieberei richten kannst.

von NegativeOne! (Gast)


Lesenswert?

Hallo zusammen,

vielen Dank für die Hilfestellung. Ich habe aus dem ein oder anderen 
Kommentar eine Lösung geformt, die mir vorallem einigermaßen gut lesbar 
erscheint.
Vielleicht probiere ich die kompakteren Möglichkeiten hier auch noch 
aus, aber ich bin nicht so geübt damit und setze daher erstmal auf (für 
mich) bessere Lesbarkeit ;)
1
  hunderter  |= !(firstbyte & (1 << 0)) << 0;
2
  hunderter  |= !(firstbyte & (1 << 2)) << 1;
3
  hunderter  |= !(firstbyte & (1 << 1)) << 2;
4
  hunderter  |= !(firstbyte & (1 << 3)) << 3;


Marten Morten schrieb:
> Wenn du wirklich nur zwei Bytes bekommst, dann können Zehner und Einer
> so nicht im zweiten Byte kodiert sein.

Stimmt - Formatierungsfehler. Sorry... so wäre es richtig:
1
  // Zehner:
2
  // 00000000
3
  // ||||
4
  // |||+------- !secondbyte.4
5
  // ||+-------- !secondbyte.6
6
  // |+--------- !secondbyte.5
7
  // +---------- !secondbyte.7
8
9
  // Einer:
10
  // 00000000
11
  //     ||||
12
  //     |||+--- !secondbyte.0
13
  //     ||+---- !secondbyte.2
14
  //     |+----- !secondbyte.1
15
  //     +------ !secondbyte.3

Vielen Dank nochmal!

von NegativeOne! (Gast)


Lesenswert?

Wolfgang schrieb:
> Da wurden beim Routen der Platine ein paar Minuten eingespart, die
> Entwicklungsarbeit wurde verkürzt. Die Software muss es jetzt ausbaden.
> ;-(

Ich fürchte, genau das ist wurde hier gemacht. Oder ein Fall von 
"Security by Obscurity"? ;)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Lesenswert?

Jens M. schrieb:
> Oh ja, eine LUT. Noch mehr Speicher und noch komplizierter. Und dann
> noch maskieren und schieben.
Irgendwie trotzdem kompakt:
1
//                           Bits 1 und 2 im Nibble vertauscht!
2
//                    {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
3
uint16_t remap[16] =  {0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15};
4
5
//                 Hunderter             Zehner                          Einer
6
uint16_t result =  remap[firstbyte]<<8 | remap[(secondbyte>>4)&0xf]<<4 | remap[secondbyte&0xf];
Dazu auch ein Screenshot vom onlinegdb.com C-Compiler.

: Bearbeitet durch Moderator
von H.Joachim S. (crazyhorse)


Lesenswert?

Jens M. schrieb:
> Oh ja, eine LUT. Noch mehr Speicher und noch komplizierter. Und dann
> noch maskieren und schieben.

Schieben musst du auch...
Komplizierter ist es nicht.
Und ob mehr Speicher verbraucht wird, wäre erst mal zu überprüfen. Der 
Unterschied wird minimal sein, vielleicht ist es sogar kompakter.
Spielt hier zwar keine Rolle, aber Laufzeit ist mit Sicherheit kleiner.

einer=tab[secondbyte & 0x0f];
zehner=tab[secondbyte>>4];
hunderter=tab[firstbyte & 0x0f];

Aber ich schiebe es trotzdem nicht durch den Compiler, beides geht.

Ok, da war Lothar schneller :-)

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Lesenswert?

H.Joachim S. schrieb:
> zehner=tab[secondbyte>>4];
Stimmt, da war ich zu übervorsichtig, da muss man eigentlich nichts 
maskieren... ;-)

Dahingehend korrigiert sieht mein Vorschlag so aus:
1
//                 Hunderter             Zehner                    Einer
2
uint16_t result =  remap[firstbyte]<<8 | remap[secondbyte>>4]<<4 | remap[secondbyte&0xf];
Das Ergebnis bleibt sich gleich.

: Bearbeitet durch Moderator
von Andreas B. (bitverdreher)


Lesenswert?

Falls Dich Tabellen stören:

zahlneu = (zahl&249) | ((zahl & 2)<<1) | ((zahl & 4)>>1))

von Nick M. (Gast)


Lesenswert?

Die Methode mit dem remap ist jedenfalls die lesbarste von Allen.
Ja, braucht mehr Platz und ist wohl auch nicht so schnell wie maskieren 
und shiften.
Aber jedenfalls eleganter als die 12 if aus der 1. Antwort (OMG!)


Nick

von H.Joachim S. (crazyhorse)


Lesenswert?

Er wills ja gar nicht in eine 16bit-Zahl haben, sondern in 3 einzelne 
chars.

NegativeOne! schrieb:
> und dabei auf drei 8-bit Variablen verteilen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Nick M. schrieb:
> Ja, braucht mehr Platz und ist wohl auch nicht so schnell wie maskieren
> und shiften.
Worauf basieren diese Annahmen?
Der Compiler kann so einen Arrayzugriff recht effizient umsetzen...
Das Array braucht einmalig Speicher (und wenn der unbedingt gespart 
werden muss, dann kann man da auch ein uint8_t Array draus machen, muss 
dann aber vor dem <<8 noch auf uint16_t casten, denn denn sonst kommt da 
sicher 0 raus).

H.Joachim S. schrieb:
> wills ja gar nicht in eine 16bit-Zahl haben, sondern in 3 einzelne chars.
Warts ab, was danach noch kommt. Üblicherweise braucht man solche Werte 
im Programm nämlich "am Stück" in einer einzigen Variablen. Nenne es 
einfach "Hellsehen"...  ;-)

: Bearbeitet durch Moderator
von Andreas B. (bitverdreher)


Lesenswert?

Lothar M. schrieb:
> Warts ab, was danach noch kommt. Üblicherweise braucht man solche Werte
> im Programm nämlich "am Stück" in einer einzigen Variablen. Nenne es
> einfach "Hellsehen"...  ;-)

Nee, glaube ich nicht. Er redet von BCD codieren. Das habe ich aber auch 
überlesen.

von Nick M. (Gast)


Lesenswert?

Lothar M. schrieb:
> Worauf basieren diese Annahmen?

Auf Erfahrung. Aber man kann ja den Code compilieren und die Größe 
beider Varianten ansehen.

Nick

von H.Joachim S. (crazyhorse)


Lesenswert?

Beides möglich :-)

Weil ich ja so gern recht habe:
Lieber schuchkleisser, dein if/foo/bar-Gekröse braucht 28Byte mehr.
Soviel zu voreiligen Behauptungen :-)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas B. schrieb:
> Er redet von BCD codieren.
Dann dürfte der Schaltern nur 10 Stellungen haben und dann muss man die 
Umrechnung in 1 einzigen Wert so machen:
1
uint16_t result =  remap[firstbyte]*100 + remap[secondbyte>>4]*10 + remap[secondbyte&0xf];

von H.Joachim S. (crazyhorse)


Lesenswert?

Lothar M. schrieb:
> Dann dürfte der Schaltern nur 10 Stellungen haben

hat er ja auch:
NegativeOne! schrieb:
> Über drei BCD Codierschalter soll eine Dezimalzahl eingestellt
> werden...0-999.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

H.Joachim S. schrieb:
> hat er ja auch:
> NegativeOne! schrieb:
>> Über drei BCD Codierschalter soll eine Dezimalzahl eingestellt
>> werden...0-999.
Da passt ja mein letzter Vorschlag wie die Faust ins Auge: als result 
kommt genau so eine Zahl heraus... ;-)

Auf dem AVR braucht dieses Programm dann 280 Bytes im Programmspeicher 
und 20 im Datenspeicher:
1
#include <avr/io.h>
2
#include <stdio.h>
3
#include <stdint.h>
4
5
uint8_t firstbyte  = 0x01; // Hex Eingabe = 1, 2, 3
6
uint8_t secondbyte = 0x45;
7
uint16_t result;
8
9
//                           Bits 1 und 2 vertauscht!
10
//                    {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
11
uint8_t remap[16] =   {0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15};
12
13
int main()
14
{
15
  //        Hunderter                        Zehner                    Einer
16
  result =  (uint16_t)remap[firstbyte]*100 + remap[secondbyte>>4]*10 + remap[secondbyte&0xf];
17
18
  while(1);
19
}
Die leere main(), also mit auskommentierter result Berechnung, braucht 
172 Bytes im Programmspeicher und 0 Bytes bei den Daten. Verbleiben 108 
Bytes für die Berechnung.

Die angepaste Lösung mit der if-Abfrage der Werte braucht 286 Bytes 
Programm und 4 Bytes Daten, was abzüglich der leeren main() dann 114 
Bytes Programmspeicherbedarf ergibt:
1
#include <avr/io.h>
2
#include <stdio.h>
3
#include <stdint.h>
4
5
uint8_t firstbyte  = 0x01; // Hex Eingabe = 1, 2, 3
6
uint8_t secondbyte = 0x45;
7
uint16_t result;
8
9
int main()
10
{
11
  unsigned char foo;
12
  unsigned char bar=0;
13
  
14
  foo = firstbyte;
15
  if (foo & 0x01) bar += 0x1;
16
  if (foo & 0x02) bar += 0x4;
17
  if (foo & 0x04) bar += 0x2;
18
  if (foo & 0x08) bar += 0x8;
19
  result = bar*100;
20
21
  foo = secondbyte>>4;
22
  bar = 0;
23
  if (foo & 0x01) bar += 0x1;
24
  if (foo & 0x02) bar += 0x4;
25
  if (foo & 0x04) bar += 0x2;
26
  if (foo & 0x08) bar += 0x8;
27
  result += bar*10;
28
29
  foo = secondbyte&0xf;
30
  bar = 0;
31
  if (foo & 0x01) bar += 0x1;
32
  if (foo & 0x02) bar += 0x4;
33
  if (foo & 0x04) bar += 0x2;
34
  if (foo & 0x08) bar += 0x8;
35
  result += bar;  
36
  
37
  while(1);
38
}

: Bearbeitet durch Moderator
von H.Joachim S. (crazyhorse)


Lesenswert?

Fast, er will keine 16bit-Zahl.
Eigentlich war die Ursprungsfrage/Ziel absolut vollständig gestellt, das 
ist selten hier :-)
-3 BCD-Schalter werden seriell in 2 Byte eingelesen
-die Schalter liefern negative Logik
-die Bitpositionen sind verwurstet, aber bei allen gleich
-Ziel: 3 8bit-Variablen mit je 0..9, also unpacked BCD

von Wilhelm M. (wimalopaan)


Lesenswert?

Und wer die LUT für beliebige Vertauschungen im Layout nicht zu Fuß 
erstellen möchte (und auch zusätzlich noch eine Laufzeitfunktion zum 
Vertauschen beliebiger Bits haben möchte), kann folgendes machen 
(Erstellen der LUT zur Compile-Zeit):
1
#include <cstddef>
2
#include <array>
3
4
template<uint8_t Bit1, uint8_t Bit2, typename T>
5
inline constexpr T swapBits(T v) {
6
    static_assert(Bit1 != Bit2);
7
    static_assert(Bit1 < sizeof(T) * 8);
8
    static_assert(Bit2 < sizeof(T) * 8);
9
    
10
    constexpr T mask1{(1 << Bit1)};
11
    constexpr T mask2{(1 << Bit2)};
12
    constexpr T mask{mask1 | mask2};
13
    
14
    return T(v & ~mask) | T(((v & mask1) >> Bit1) << Bit2) | T(((v & mask2) >> Bit2) << Bit1);
15
}
16
17
constinit auto lut = []{
18
    using lut_type = std::array<std::byte, 16>;
19
    using size_type = lut_type::size_type;
20
    lut_type l;
21
    for(size_type i = 0; i < l.size(); ++i) {
22
        l[i] = swapBits<1,2>(std::byte{i});
23
    }
24
    return l;
25
}();
26
27
volatile std::byte firstByte;
28
volatile std::byte secondByte;
29
30
int main() {
31
    auto c1 = lut[uint8_t(firstByte & 0x0f_B)];
32
    auto c2 = lut[uint8_t(secondByte >> 4)];
33
    auto c3 = lut[uint8_t(secondByte & 0x0f_B)];
34
    
35
    while(true);
36
}

von Jens M. (schuchkleisser)


Lesenswert?

H.Joachim S. schrieb:
> Lieber schuchkleisser, dein if/foo/bar-Gekröse braucht 28Byte mehr.

Wo?

Im PIC geht das mit insgesamt 27 Instruktionen/Words.
Das Compiler so dämlich sind und das nicht können, und/oder das die 
Architektur hier nicht genannt wurde, dafür kann ich nix.
LUT und Schieberei sind da definitiv größer. Das mag unter bestimmten 
Prozessoren egal sein, aber ich bin ein Freund kompakten Codes, und 
damit mein ich nicht das geschriebene, sondern das Ergebnis. Was nicht 
bedeuten muss das es unleserlich wird.

Das mein Vorschlag natürlich ebenso gleich die dezimalisierung 
übernehmen kann ist klar.
Und seid mir nicht böse, aber die Umsetzung der Tabelle (4bit) in ein 
uint16 und die Schieberei der Bits finde ich alles andere als 
übersichtlich.
Wenn da steht (Schalter a, Bit 1 = tausender bit 3) und man dann 
"x+=800;" schreibt, da weiß man wenigstens wie der Wert berechnet wurde.
Auch wenn ich zugeben muss, das es als Zweizeiler sehr kompakt 
geschrieben ist.

Wobei die Gesamtlösung auch z.B. vom Schaltplan abhängt: sind wirklich 
alle 3 Schalter gleich vertauscht? Wenn nicht, braucht man 2 oder 3 
LUTs.
Aber bitte, jeder hat seins.
Meine Lösung hat z.B. den Vorteil, beliebig viele Bits aus beliebigen 
Bytes in andere beliebige Bytes transferieren zu können, inklusive 
teilweiser negierung oder dezimalisierung.
Das erleichtert die Layouterstellung enorm, macht den Schaltplan aber 
nicht unbedingt lesbarer...

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

H.Joachim S. schrieb:
> -die Schalter liefern negative Logik
Also vorher noch invertieren ;-)
> -die Bitpositionen sind verwurstet, aber bei allen gleich
> -Ziel: 3 8bit-Variablen mit je 0..9, also unpacked BCD
Dieses "Ziel" erscheint mir angesichts der Formulierung "ich muss 
daher..." eher als "Zwischenziel", das dem sequenziellen Programm- und 
Lösungsablauf geschuldet ist.

Finales Ziel dürfte die Aufgabenstellung in der ersten Zeile sein, wo
NegativeOne! schrieb:
>>> soll eine Dezimalzahl eingestellt werden...0-999

Aber warten wirs ab...

: Bearbeitet durch Moderator
von Jens M. (schuchkleisser)


Lesenswert?

Lothar M. schrieb:
> foo = secondbyte&0xf;
>   bar = 0;
>   if (foo & 0x01) bar += 0x1;
>   if (foo & 0x02) bar += 0x4;
>   if (foo & 0x04) bar += 0x2;
>   if (foo & 0x08) bar += 0x8;
>   result += bar;

weil das ja auch gar nicht f-ing uneffizient implementiert ist.
Wenn man direkt if (secondbyte &0x..) macht und direkt (result += ..), 
spart man die Maskierung, Zuweisungen und die Multiplikation ein, was 
den Code verkürzt und die Lesbarkeit nicht verschlechtert.
Teste das bitte nochmal, aber diesmal mit nicht auf "ich beweis das 
meine Lösung kürzer ist"-Voreinstellungen... ;)

Wilhelm M. schrieb:
> Und wer die LUT für beliebige Vertauschungen im Layout nicht zu Fuß
> erstellen möchte (und auch zusätzlich noch eine Laufzeitfunktion zum
> Vertauschen beliebiger Bits haben möchte), kann folgendes machen
> (Erstellen der LUT zur Compile-Zeit):

Ach du Sch....
Das ist weder lesbarer, noch kürzer. Mit Glück flexibler...
Wie lange muss man im dunklen Keller C büffeln bis man so ein Monstrum 
lesen und konstruieren kann?

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jens M. schrieb:
> Wie lange muss man im dunklen Keller C büffeln bis man so ein Monstrum
> lesen und konstruieren kann?

C hilft da nicht weiter. Das ist C++. Und auch nicht jedes x-beliebige, 
sondern ein neuerer Dialekt, wenn nicht sogar der allerneueste. Damit 
verhindert man erfolgreich, dass man das Programm-Beispiel mit 
demjenigen gcc-Compiler zum Laufen bringt, den man gerade so zufällig 
auf dem Rechner rumfliegen hat. Wenn Wilhelm seine C++-Beispiele 
auspackt, ist meistens erstmal eine Neuinstallation angesagt, um das 
nachvollziehen zu können.

Möchte das vielleicht noch jemand in Oberon oder Python formulieren, um 
noch mehr Unsicherheit für den TO zu schaffen?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jens M. schrieb:
> Teste das bitte nochmal, aber diesmal mit nicht auf "ich beweis das
> meine Lösung kürzer ist"-Voreinstellungen...
Häh, wo habe ich das gemacht? Ich habe doch sogar bewiesen, dass die 
if-Afragerei nur ein Fünftel des Datenspeichers braucht.

Das Weglassen der foo ändert gar nichts. So schlau ist der Compiler 
offenbar sowieso, dass er die Berechnungen nicht 4 mal macht, sondern 
einen Zwischenwert ausrechnet:
1
int main()
2
{
3
  unsigned char bar=0;
4
  
5
  if (firstbyte & 0x01) bar += 0x1;
6
  if (firstbyte & 0x02) bar += 0x4;
7
  if (firstbyte & 0x04) bar += 0x2;
8
  if (firstbyte & 0x08) bar += 0x8;
9
  result = bar*100;
10
11
  bar = 0;
12
  if (secondbyte>>4 & 0x01) bar += 0x1;
13
  if (secondbyte>>4 & 0x02) bar += 0x4;
14
  if (secondbyte>>4 & 0x04) bar += 0x2;
15
  if (secondbyte>>4 & 0x08) bar += 0x8;
16
  result += bar*10;
17
18
  bar = 0;
19
  if (secondbyte&0xf & 0x01) bar += 0x1;
20
  if (secondbyte&0xf & 0x02) bar += 0x4;
21
  if (secondbyte&0xf & 0x04) bar += 0x2;
22
  if (secondbyte&0xf & 0x08) bar += 0x8;
23
  result += bar;  
24
  
25
  while(1);
26
}
Ergebnis:
Program Memory Usage   :  286 bytes
Data Memory Usage   :  4 bytes


Und diese Lösung:
1
int main()
2
{
3
  result = 0;
4
  if (firstbyte & 0x01) result += 100;
5
  if (firstbyte & 0x02) result += 400;
6
  if (firstbyte & 0x04) result += 200;
7
  if (firstbyte & 0x08) result += 800;
8
  if (secondbyte>>4 & 0x01)  result += 10;
9
  if (secondbyte>>4 & 0x02)  result += 40;
10
  if (secondbyte>>4 & 0x04)  result += 20;
11
  if (secondbyte>>4 & 0x08)  result += 80;
12
  if (secondbyte&0xf & 0x01) result += 1;
13
  if (secondbyte&0xf & 0x02) result += 4;
14
  if (secondbyte&0xf & 0x04) result += 2;
15
  if (secondbyte&0xf & 0x08) result += 8;
16
  
17
  while(1);
18
}
braucht:
Program Memory Usage   :  432 bytes
Data Memory Usage   :  4 bytes


Bewertung (samt Abzug der leeren main() Funktion) bitte selber 
durchführen, sonst wird nur wieder der Überbringer der Botschaft 
gesteinigt.

Der Grund für den großen Codebedarf der letzten Variante liegt darin, 
dass die ganzen zu addierenden unterschiedlichen Zahlen 1,2,...800 ja 
alle als 16-Bit Integer in den Code müssen.

: Bearbeitet durch Moderator
von leo (Gast)


Lesenswert?

Wilhelm M. schrieb:
> template<uint8_t Bit1, uint8_t Bit2, typename T>

und weitere ~30 Zeilen.

Wie immer die laengste, komlizierteste, unleserlichste und unnoetigste 
"Loesung" weit und breit ;-)

leo

von Jens M. (schuchkleisser)


Lesenswert?

Danke für den Beweis.
OmfG was sind Compiler schlecht.

PIC kann so (Man möge mir verzeihen, AVR-Assembler kann ich nicht):
1
clrf result1
2
clrf result2
3
clrf result3
4
btfss firstbyte,x
5
bsf result1,y
6
btfss firstbyte,x
7
bsf result1,y
8
btfss firstbyte,x
9
bsf result1,y
10
...
Abseits der vorhandenen RAMs für die Eingangs- und Ausgangswerte kein 
weiterer RAM-Verbrauch und nur 27 Flashzellen.
In AVR sollte das ähnlich gehen, der hat allerdings ja auch 
mehrbyte-Befehle.
Gibts da BitTest und BitSet?

von Wilhelm M. (wimalopaan)


Lesenswert?

leo schrieb:
> Wilhelm M. schrieb:
>> template<uint8_t Bit1, uint8_t Bit2, typename T>
>
> und weitere ~30 Zeilen.
>
> Wie immer die laengste, komlizierteste, unleserlichste und unnoetigste
> "Loesung" weit und breit ;-)

Naja, swapBits<> steckt man einmal in seinem Leben irgendwo in eine 
Headerdatei. Fertig.

Die Generierung der LUT verhindert Copy-n-Paste-Fehler oder schlichtes 
Verrechnen.

Zusätzlich hat man hier eine beliebig konfigurierbare Lösung für 
beliebige Vertauschungen im Layout.
Und man kann wählen, welchen Speed <-> Memory-Tradeoff man haben will 
(bis zu dem Punkt, wo die LUT 256 Einträge hat). Flexibler geht es kaum. 
Und die LUT-Lösung ist die kürzeste und schnellste.

Beitrag #6005344 wurde vom Autor gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

Jens M. schrieb:

> Gibts da BitTest und BitSet?

Na klar, und die benutzt dre Compiler auch.

von Marten Morten (Gast)


Lesenswert?

Lothar M. schrieb:
> Andreas B. schrieb:
>> Er redet von BCD codieren.
> Dann dürfte der Schaltern nur 10 Stellungen haben und dann muss man die
> Umrechnung in 1 einzigen Wert so machen:
>
1
> uint16_t result =  remap[firstbyte]*100 + remap[secondbyte>>4]*10 + 
2
> remap[secondbyte&0xf];
3
>

Viel zu einfach :-)

Da es im Thread nur noch um das Aufblasen von Code geht würde ich für 
die Konvertierung von BCD nach Binär den Reverse Double Dabble 
Algorithmus vorschlagen.

Da kommt anständig was zusammen. Bei drei BCD Ziffern sind das für die 
Wandlung bis zu 10 Iterationen mit je einem Shift pro Iteration. Dazu 
pro BCD Ziffer ein Vergleich pro Iteration, also drei pro Iteration, 
eventuell mit vorheriger Maskierung. Nicht zu vergessen je nach Ergebnis 
des Vergleichs eine Subtraktion pro BCD Ziffer und Iteration, also 
maximal 30 Subtraktionen. Zum Schluss das Nachjustieren des Ergebnisses 
mit Shifts wenn weniger als zehn Iterationen benötigt wurden. So macht 
Code mit Bit-Shiften richtig was her :-)

von guest (Gast)


Lesenswert?

Lothar M. schrieb:
>   if (secondbyte>>4 & 0x01)  result += 10;
>   if (secondbyte>>4 & 0x02)  result += 40;
>   if (secondbyte>>4 & 0x04)  result += 20;
>   if (secondbyte>>4 & 0x08)  result += 80;

Wozu denn die Bitshifterei?
1
  if (secondbyte & 0x10)  result += 10;
2
  if (secondbyte & 0x20)  result += 40;
3
  if (secondbyte & 0x40)  result += 20;
4
  if (secondbyte & 0x80)  result += 80;

: Bearbeitet durch Moderator
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

guest schrieb:
> Wozu denn die Bitshifterei?
So:
1
int main()
2
{
3
  result = 0;
4
  if (firstbyte & 0x01) result += 100;
5
  if (firstbyte & 0x02) result += 400;
6
  if (firstbyte & 0x04) result += 200;
7
  if (firstbyte & 0x08) result += 800;
8
  if (secondbyte & 0x10)  result += 10;
9
  if (secondbyte & 0x20)  result += 40;
10
  if (secondbyte & 0x40)  result += 20;
11
  if (secondbyte & 0x80)  result += 80;
12
  if (secondbyte&0xf & 0x01) result += 1;
13
  if (secondbyte&0xf & 0x02) result += 4;
14
  if (secondbyte&0xf & 0x04) result += 2;
15
  if (secondbyte&0xf & 0x08) result += 8;
16
  
17
  while(1);
18
}
sinds dann
Program Memory Usage :  426 bytes
Data Memory Usage    :  4 bytes

von Jens M. (schuchkleisser)


Lesenswert?

426 Bytes, ein Horror.
Wird das weniger, wenn man statt der Addition den Compiler mit der Nase 
drauf stößt und ein Or benutzt?
Ich denke, der hat tatsächlich eine gestackte 16-Bit-Addition mit 
eingebaut und merkt sich 12 16bit große Integer, anstatt einfach ein 
BitSet zu machen.

von Peter D. (peda)


Lesenswert?

Man kann das noch weiter optimieren, indem man zuerst das Low-Byte 
berechnet:
1
uint16_t bcd3(uint16_t in)
2
{
3
  uint8_t outl = 0;
4
  if (in & 0x0001) outl += 100;
5
  if (in & 0x1000) outl += 10;
6
  if (in & 0x2000) outl += 40;
7
  if (in & 0x4000) outl += 20;
8
  if (in & 0x8000) outl += 80;
9
  if (in & 0x0100) outl += 1;
10
  if (in & 0x0200) outl += 4;
11
  if (in & 0x0400) outl += 2;
12
  if (in & 0x0800) outl += 8;
13
  uint16_t out = outl;
14
  if (in & 0x0002) out += 400;
15
  if (in & 0x0004) out += 200;
16
  if (in & 0x0008) out += 800;
17
  return out;
18
}

Sind auf dem AVR 72 Byte Code.

von Peter D. (peda)


Lesenswert?

Jens M. schrieb:
> Wird das weniger, wenn man statt der Addition den Compiler mit der Nase
> drauf stößt und ein Or benutzt?

Nein, weil das falsch ist. Er will ein int als Ergebnis, damit er damit 
rechnen kann.
Ich kenne keinen C-Compiler, der intern mit packed BCD rechnet.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Sind auf dem AVR 72 Byte Code.

Worum geht es denn: Ausführungszeit, Flash-Size, ggf. RAM-Size, 
Wartbarkeit, ...?

von Jens M. (schuchkleisser)


Lesenswert?

Peter D. schrieb:
> Sind auf dem AVR 72 Byte Code.

Alleine nur, weil keine 16bit-Addition dabei ist. Nice!

Peter D. schrieb:
> Nein, weil das falsch ist. Er will ein int als Ergebnis, damit er damit
> rechnen kann.

Was ist der Unterschied zwischen "Addiere 0x40" und "Setze Bit 6"?
Beides unter der Voraussetzung, das der Wert vorher genullt wurde...
Richtig: keiner. Nur ist der Compiler so dämlich das er das nicht merkt 
und fröhlich den (leeren) 16-bit-Wert "0" auf den Stack schiebt, dann 
den zu addierenden "0x0040" hinterher, dann kommt der Call der 
offensichtlich ncht gerade kleinen Addierungsroutine, anschließend wird 
der Wert des Ergebnisses vom Stack in den Variablenspeicher kopiert.
All das ist ein einziger BitSet-Befehl.
Der Peda hat's ja schon gezeigt, alleine nur die 8-Bit-Rechnung spart 
schon ~80% Code ein.

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Worum geht es denn: Ausführungszeit, Flash-Size, ggf. RAM-Size,
> Wartbarkeit, ...?

Diese Variante ist in allen 4 Punkten optimal.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jens M. schrieb:
> Wenn man direkt if (secondbyte &0x..) macht und direkt (result += ..),
> spart man die Maskierung, Zuweisungen und die Multiplikation ein
Da sieht man, was der Compiler draus macht:
1
  result = 0;
2
  a6:  10 92 03 01   sts  0x0103, r1  ; 0x800103 <__data_end+0x1>
3
  aa:  10 92 02 01   sts  0x0102, r1  ; 0x800102 <__data_end>
4
  if (firstbyte & 0x01) result += 100;
5
  ae:  80 91 01 01   lds  r24, 0x0101  ; 0x800101 <firstbyte>
6
  b2:  80 ff         sbrs  r24, 0
7
  b4:  06 c0         rjmp  .+12       ; 0xc2 <main+0x1c>
8
  b6:  24 e6         ldi  r18, 0x64  ; 100
9
  b8:  30 e0         ldi  r19, 0x00  ; 0
10
  ba:  30 93 03 01   sts  0x0103, r19  ; 0x800103 <__data_end+0x1>
11
  be:  20 93 02 01   sts  0x0102, r18  ; 0x800102 <__data_end>
12
  if (firstbyte & 0x02) result += 400;
13
  c2:  81 ff         sbrs  r24, 1
14
  c4:  0a c0         rjmp  .+20       ; 0xda <main+0x34>
15
  c6:  20 91 02 01   lds  r18, 0x0102  ; 0x800102 <__data_end>
16
  ca:  30 91 03 01   lds  r19, 0x0103  ; 0x800103 <__data_end+0x1>
17
  ce:  20 57         subi  r18, 0x70  ; 112
18
  d0:  3e 4f         sbci  r19, 0xFE  ; 254
19
  d2:  30 93 03 01   sts  0x0103, r19  ; 0x800103 <__data_end+0x1>
20
  d6:  20 93 02 01   sts  0x0102, r18  ; 0x800102 <__data_end>
21
  if (firstbyte & 0x04) result += 200;
22
  da:  82 ff         sbrs  r24, 2
23
  dc:  0a c0         rjmp  .+20       ; 0xf2 <main+0x4c>
24
  de:  20 91 02 01   lds  r18, 0x0102  ; 0x800102 <__data_end>
25
  e2:  30 91 03 01   lds  r19, 0x0103  ; 0x800103 <__data_end+0x1>
26
  e6:  28 53         subi  r18, 0x38  ; 56
27
  e8:  3f 4f         sbci  r19, 0xFF  ; 255
28
  ea:  30 93 03 01   sts  0x0103, r19  ; 0x800103 <__data_end+0x1>
29
  ee:  20 93 02 01   sts  0x0102, r18  ; 0x800102 <__data_end>
Er verwendet natürlich den sbrs Befehl. Aber er muss nach jeder 
if-Abfrage den Wert mit 16 Bit berechnen und gleich darauf abspeichern, 
weil es ja sein könnte, dass keine der nachfolgenden if-Abfragen mehr 
greift.

Die Variante mit der direkten Abfrage von firstbyte/secondbyte und 
Berechnung auf der lokalen Variabeln bar im 
Beitrag "Re: Bits shiften - ich verstehe es nicht." 
enthält noch einen Fehler: bar ist nur 8 Bit breit.
Mit der korrigierten Variante hier
1
int main()
2
{
3
  uint16_t bar=0;
4
  
5
  if (firstbyte & 0x01) bar += 0x1;
6
  if (firstbyte & 0x02) bar += 0x4;
7
  if (firstbyte & 0x04) bar += 0x2;
8
  if (firstbyte & 0x08) bar += 0x8;
9
  result = bar*100;
10
11
  bar = 0;
12
  if (secondbyte>>4 & 0x01) bar += 0x1;
13
  if (secondbyte>>4 & 0x02) bar += 0x4;
14
  if (secondbyte>>4 & 0x04) bar += 0x2;
15
  if (secondbyte>>4 & 0x08) bar += 0x8;
16
  result += bar*10;
17
18
  bar = 0;
19
  if (secondbyte&0xf & 0x01) bar += 0x1;
20
  if (secondbyte&0xf & 0x02) bar += 0x4;
21
  if (secondbyte&0xf & 0x04) bar += 0x2;
22
  if (secondbyte&0xf & 0x08) bar += 0x8;
23
  result += bar;
24
  
25
  while(1);
26
}
ergibt sich dann natürlich Mehraufwand:
Program Memory Usage   :  312 bytes
Data Memory Usage   :  4 bytes

Und das ist ein Ausschnitt fürs Hunderternibble vom Assemblercode:
1
int main()
2
{
3
  uint16_t bar=0;
4
  aa:  42 2f         mov  r20, r18
5
  ac:  41 70         andi  r20, 0x01  ; 1
6
  ae:  50 e0         ldi  r21, 0x00  ; 0
7
  b0:  ca 01         movw  r24, r20
8
  
9
  if (firstbyte & 0x01) bar += 0x1;
10
  if (firstbyte & 0x02) bar += 0x4;
11
  b2:  21 fd         sbrc  r18, 1
12
  b4:  04 96         adiw  r24, 0x04  ; 4
13
  if (firstbyte & 0x04) bar += 0x2;
14
  b6:  22 fd         sbrc  r18, 2
15
  b8:  02 96         adiw  r24, 0x02  ; 2
16
  if (firstbyte & 0x08) bar += 0x8;
17
  ba:  23 fd         sbrc  r18, 3
18
  bc:  08 96         adiw  r24, 0x08  ; 8
19
  result = bar*100;
20
  be:  24 e6         ldi  r18, 0x64  ; 100
21
  c0:  28 9f         mul  r18, r24
22
  c2:  a0 01         movw  r20, r0
23
  c4:  29 9f         mul  r18, r25
24
  c6:  50 0d         add  r21, r0
25
  c8:  11 24         eor  r1, r1

Und zu guter Letzt nochmal die LUT-Variante, bei der vorrangig die 
beiden Multiplikationen (und dabei besonders die *10 mit ihren vielen 
Additionen) hervorstechen:
1
int main()
2
{
3
  result =  remap[firstbyte]*100  + remap[secondbyte>>4]*10 + remap[secondbyte&0xf]; // 278-172 = 106 Bytes
4
  a6:  20 91 10 01   lds  r18, 0x0110  ; 0x800110 <secondbyte>
5
  aa:  e0 91 11 01   lds  r30, 0x0111  ; 0x800111 <firstbyte>
6
  ae:  f0 e0         ldi  r31, 0x00  ; 0
7
  b0:  e0 50         subi  r30, 0x00  ; 0
8
  b2:  ff 4f         sbci  r31, 0xFF  ; 255
9
  b4:  30 81         ld  r19, Z
10
  b6:  e2 2f         mov  r30, r18
11
  b8:  e2 95         swap  r30
12
  ba:  ef 70         andi  r30, 0x0F  ; 15
13
  bc:  f0 e0         ldi  r31, 0x00  ; 0
14
  be:  e0 50         subi  r30, 0x00  ; 0
15
  c0:  ff 4f         sbci  r31, 0xFF  ; 255
16
  c2:  80 81         ld  r24, Z
17
  c4:  90 e0         ldi  r25, 0x00  ; 0
18
  c6:  ac 01         movw  r20, r24
19
  c8:  44 0f         add  r20, r20
20
  ca:  55 1f         adc  r21, r21
21
  cc:  88 0f         add  r24, r24
22
  ce:  99 1f         adc  r25, r25
23
  d0:  88 0f         add  r24, r24
24
  d2:  99 1f         adc  r25, r25
25
  d4:  88 0f         add  r24, r24
26
  d6:  99 1f         adc  r25, r25
27
  d8:  84 0f         add  r24, r20
28
  da:  95 1f         adc  r25, r21
29
  dc:  44 e6         ldi  r20, 0x64  ; 100
30
  de:  34 9f         mul  r19, r20
31
  e0:  80 0d         add  r24, r0
32
  e2:  91 1d         adc  r25, r1
33
  e4:  11 24         eor  r1, r1
34
  e6:  e2 2f         mov  r30, r18
35
  e8:  ef 70         andi  r30, 0x0F  ; 15
36
  ea:  f0 e0         ldi  r31, 0x00  ; 0
37
  ec:  e0 50         subi  r30, 0x00  ; 0
38
  ee:  ff 4f         sbci  r31, 0xFF  ; 255
39
  f0:  20 81         ld  r18, Z
40
  f2:  82 0f         add  r24, r18
41
  f4:  91 1d         adc  r25, r1
42
  f6:  90 93 13 01   sts  0x0113, r25  ; 0x800113 <__data_end+0x1>
43
  fa:  80 93 12 01   sts  0x0112, r24  ; 0x800112 <__data_end>
Wobei hier bereits das Abholen der Variabeln und Abspeichern des 
Ergebnisses 16 Bytes brauchen... ;-)

Jens M. schrieb:
> Der Peda hat's ja schon gezeigt, alleine nur die 8-Bit-Rechnung spart
> schon ~80% Code ein.
Nur kommt da halt keiner drauf. Oder eben nur einer von vielen mit dem 
Hintergrundwissen, dass man dem Compiler "nach dem Maul" programmieren 
und zuerst so lange wie möglich mit einem 8-Bit Wert rechnen muss.


Peter D. schrieb:
> Sind auf dem AVR 72 Byte Code.
Ich habe diesen "Netto"-Code mal angewendet und komme in der realen 
"Brutto"-Anwendung wie hier:
1
uint16_t bcd3(uint16_t in)
2
{
3
  uint8_t outl = 0;
4
  if (in & 0x0001) outl += 100;
5
  if (in & 0x1000) outl += 10;
6
  if (in & 0x2000) outl += 40;
7
  if (in & 0x4000) outl += 20;
8
  if (in & 0x8000) outl += 80;
9
  if (in & 0x0100) outl += 1;
10
  if (in & 0x0200) outl += 4;
11
  if (in & 0x0400) outl += 2;
12
  if (in & 0x0800) outl += 8;
13
  uint16_t out = outl;
14
  if (in & 0x0002) out += 400;
15
  if (in & 0x0004) out += 200;
16
  if (in & 0x0008) out += 800;
17
  return out;
18
}
19
20
int main()
21
{
22
  result =  bcd3((uint16_t)firstbyte<<8 + secondbyte);
23
24
  while(1);
25
}
auf
Program Memory Usage   :  288 bytes
Data Memory Usage   :  4 bytes


Dann habe ich die LUT-Variante zum ernsthaften Vergleich auch mal 
umgebaut und einen Funktionsaufruf draus gemacht:
1
uint8_t remap[16] =   {0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15};
2
3
uint16_t bcd3(uint8_t firstbyte, uint8_t secondbyte)
4
{
5
  return remap[firstbyte]*100  + remap[secondbyte>>4]*10 + remap[secondbyte&0xf];;
6
}
7
8
int main()
9
{
10
  result =  bcd3(firstbyte, secondbyte);
11
12
  while(1);
13
}
Ergebnis:
Program Memory Usage   :  290 bytes
Data Memory Usage   :  20 bytes


Schlüsse bitte selber ziehen...

Peter D. schrieb:
>> Ausführungszeit, Flash-Size, ggf. RAM-Size, Wartbarkeit, ...?
> Diese Variante ist in allen 4 Punkten optimal.
Bei der Wartbarkeit möchte ich da mal einhaken: ich bin mir 100% sicher, 
dass einige schon bei der ersten if-Abfrage in dieser Funktion ins 
Schleudern kommen und die nach unten umsortieren. Dorthin, zu den 
Hundertern, wo sie ihrem Gefühl nach auch hingehört...  ;-)

So etwa:
1
uint16_t bcd3(uint16_t in)
2
{
3
  uint8_t outl = 0;
4
  if (in & 0x1000) outl += 10;
5
  if (in & 0x2000) outl += 40;
6
  if (in & 0x4000) outl += 20;
7
  if (in & 0x8000) outl += 80;
8
  if (in & 0x0100) outl += 1;
9
  if (in & 0x0200) outl += 4;
10
  if (in & 0x0400) outl += 2;
11
  if (in & 0x0800) outl += 8;
12
  uint16_t out = outl;
13
  if (in & 0x0001) out += 100;
14
  if (in & 0x0002) out += 400;
15
  if (in & 0x0004) out += 200;
16
  if (in & 0x0008) out += 800;
17
  return out;
18
}
19
20
int main()
21
{
22
  result =  bcd3((uint16_t)secondbyte<<8 + firstbyte);
23
24
  while(1);
25
}
Ergebnis dieser Änderung im obigen Gesamtprogramm:
Program Memory Usage   :  292 bytes

: Bearbeitet durch Moderator
von Peter D. (peda)


Lesenswert?

Lothar M. schrieb:
> Aber er muss nach jeder
> if-Abfrage den Wert mit 16 Bit berechnen und gleich darauf abspeichern,
> weil es ja sein könnte, dass keine der nachfolgenden if-Abfragen mehr
> greift.

Und genau deshalb sollte man globale Variablen meiden, wie der Teufel 
das Weihwasser.

von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> Die Generierung der LUT verhindert Copy-n-Paste-Fehler
> oder schlichtes Verrechnen.

Und wenn man dem Template falsche Parameter gibt (z.B. durch 
Copy-n-Paste-Fehler oder schlichtes Verrechnen aus dem Schaltplan), dann 
sucht man daran stundenlang herum. Einfach hinschreiben macht es für den 
Menschen einfacher.

von Lutz S. (lutzs)


Lesenswert?

Interessante Diskussion, vielen Dank an alle Beteiligten.

von Marten Morten (Gast)


Lesenswert?

Lothar M. schrieb:
> Bei der Wartbarkeit möchte ich da mal einhaken: ich bin mir 100% sicher,
> dass einige schon bei der ersten if-Abfrage in dieser Funktion ins
> Schleudern kommen und die nach unten umsortieren. Dorthin, zu den
> Hundertern, wo sie ihrem Gefühl nach auch hingehört...  ;-)

Manche Programmierer verwenden Kommentare um auf solche Besonderheiten 
hinzuweisen :-)

> Ich habe diesen "Netto"-Code mal angewendet und komme in der realen
> "Brutto"-Anwendung wie hier:
>
> uint16_t bcd3(uint16_t in)
> {
>   uint8_t outl = 0;
>   if (in & 0x0001) outl += 100;
>   if (in & 0x1000) outl += 10;
>   if (in & 0x2000) outl += 40;
>   if (in & 0x4000) outl += 20;
>   if (in & 0x8000) outl += 80;
>   if (in & 0x0100) outl += 1;
>   if (in & 0x0200) outl += 4;
>   if (in & 0x0400) outl += 2;
>   if (in & 0x0800) outl += 8;
>   uint16_t out = outl;
>   if (in & 0x0002) out += 400;
>   if (in & 0x0004) out += 200;
>   if (in & 0x0008) out += 800;
>   return out;
> }
>
> int main()
> {
>   result =  bcd3((uint16_t)firstbyte<<8 + secondbyte);
>
>   while(1);
> }


Ohne es ausgemessen zu haben und für einen Compiler der keine 
Integer-Promotion von 8-Bit Argumenten macht:
1
//
2
// Convert the BCD input to an int.
3
// Accounts for the fact that bits 2 <-> 3 and 5 <-> 6 are
4
// swapped in the input due to hardware idiosyncrasies
5
//
6
uint16_t bcd3(uint8_t first, uint8_t second)
7
{
8
  // perform as many operations as possible with 8-bit 
9
  uint8_t outl = 0;
10
11
  // Tenth (second byte high nibble)
12
  if (second & 0x80) outl += 80;
13
  if (second & 0x40) outl += 20; //  \_ swapped
14
  if (second & 0x20) outl += 40; //  /
15
  if (second & 0x10) outl += 10;
16
17
  // Ones (second byte low nibble)
18
  if (second & 0x08) outl += 8;
19
  if (second & 0x04) outl += 2; //  \_ swapped
20
  if (second & 0x02) outl += 4; //  /
21
  if (second & 0x01) outl += 1;
22
23
  // Hundreds (first byte low nibble)
24
  // 100 still fits in the 8-bit outl variable, so we
25
  // add it here, not below
26
  if (first & 0x01) outl += 100;
27
28
  // Remaining values don't fit in the 8-but outl, so 
29
  // convert to 16 bit for the remaining values
30
  uint16_t out = outl;
31
32
  if (first & 0x08) out += 800;
33
  if (first & 0x04) out += 200; //  \_ swapped
34
  if (first & 0x02) out += 400; //  /
35
36
  return out;
37
}
38
39
int main()
40
{
41
  result =  bcd3(firstbyte, secondbyte);
42
43
  while(1);
44
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Und genau deshalb sollte man globale Variablen meiden, wie der Teufel
> das Weihwasser.
Danke für den Impuls... ;-)
Durch eine lokale Variable auf diese Art:
1
#include <avr/io.h>
2
#include <stdio.h>
3
#include <stdint.h>
4
5
uint8_t firstbyte  = 0x01; // Hex Eingabe = 1, 2, 3
6
uint8_t secondbyte = 0x45;
7
uint16_t result;
8
9
int main()
10
{
11
  uint16_t r = 0;
12
  if (firstbyte & 0x01)      r += 100;
13
  if (firstbyte & 0x02)      r += 400;
14
  if (firstbyte & 0x04)      r += 200;
15
  if (firstbyte & 0x08)      r += 800;
16
  if (secondbyte>>4 & 0x01)  r += 10;
17
  if (secondbyte>>4 & 0x02)  r += 40;
18
  if (secondbyte>>4 & 0x04)  r += 20;
19
  if (secondbyte>>4 & 0x08)  r += 80;
20
  if (secondbyte & 0x01)     r += 1;
21
  if (secondbyte & 0x02)     r += 4;
22
  if (secondbyte & 0x04)     r += 2;
23
  if (secondbyte & 0x08)     r += 8;
24
  result = r;
25
  
26
  while(1);
27
}
sieht das Ganze schon wesentlich besser aus:
Program Memory Usage   :  270 bytes
Data Memory Usage  :  4 bytes

Und auch die Variante mit Funktionsaufruf sieht gut aus:
1
uint16_t bcd3(uint8_t firstbyte, uint8_t secondbyte)
2
{
3
  uint16_t r = 0;
4
  if (firstbyte & 0x01)      r += 100;
5
  if (firstbyte & 0x02)      r += 400;
6
  if (firstbyte & 0x04)      r += 200;
7
  if (firstbyte & 0x08)      r += 800;
8
  if (secondbyte>>4 & 0x01)  r += 10;
9
  if (secondbyte>>4 & 0x02)  r += 40;
10
  if (secondbyte>>4 & 0x04)  r += 20;
11
  if (secondbyte>>4 & 0x08)  r += 80;
12
  if (secondbyte & 0x01)     r += 1;
13
  if (secondbyte & 0x02)     r += 4;
14
  if (secondbyte & 0x04)     r += 2;
15
  if (secondbyte & 0x08)     r += 8;
16
  return r;
17
}
18
19
int main()
20
{
21
  result =  bcd3(firstbyte, secondbyte);
22
23
  while(1);
24
}
Program Memory Usage   :  278 bytes
Data Memory Usage   :  4 bytes

Das simple Umsortieren der Abfragen und Zuweisungen analog zu Pedas 
"erst mal nur Bytebearbeitung"-Strategie spart nochmal 4 Bytes:
Program Memory Usage   :  274 bytes
1
uint16_t bcd3(uint8_t firstbyte, uint8_t secondbyte)
2
{
3
  uint16_t r = 0;
4
  if (secondbyte & 0x01)     r += 1;
5
  if (secondbyte & 0x02)     r += 4;
6
  if (secondbyte & 0x04)     r += 2;
7
  if (secondbyte & 0x08)     r += 8;
8
  if (secondbyte>>4 & 0x01)  r += 10;
9
  if (secondbyte>>4 & 0x02)  r += 40;
10
  if (secondbyte>>4 & 0x04)  r += 20;
11
  if (secondbyte>>4 & 0x08)  r += 80;
12
  if (firstbyte & 0x01)      r += 100;
13
  if (firstbyte & 0x02)      r += 400;
14
  if (firstbyte & 0x04)      r += 200;
15
  if (firstbyte & 0x08)      r += 800;
16
  return r;
17
}
18
19
int main()
20
{
21
  result =  bcd3(firstbyte, secondbyte);
22
23
  while(1);
24
}
Denn da erkennt der Compiler, dass er bei den ersten paar Rechnungen das 
höherwertige Byte nicht beackern muss...

: Bearbeitet durch Moderator
von Lutz S. (lutzs)


Lesenswert?

Marten Morten schrieb:
> if (second & 0x01) outl += 1;

Wenn man das gleich am Anfang macht kann auch die Addition noch 
entfallen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Marten Morten schrieb:
> Ohne es ausgemessen zu haben
Program Memory Usage   :  268 bytes
Das kommt allein vom Umformatieren von Pedas Code auf 2 Chars statt 1 
Short als Aufrufparameter... ;-)

Der Assembler der 2-Char-Variante:
1
uint16_t bcd3(uint8_t first, uint8_t second)
2
{
3
  // perform as many operations as possible with 8-bit
4
  uint8_t outl = 0;
5
  b0:  90 e0         ldi  r25, 0x00  ; 0
6
7
  // Tenth (second byte high nibble)
8
  if (second & 0x80) outl += 80;
9
  if (second & 0x40) outl += 20; //  \_ swapped
10
  b2:  66 fd         sbrc  r22, 6
11
  b4:  9c 5e         subi  r25, 0xEC  ; 236
12
  if (second & 0x20) outl += 40; //  /
13
  b6:  65 fd         sbrc  r22, 5
14
  b8:  98 5d         subi  r25, 0xD8  ; 216
15
  if (second & 0x10) outl += 10;
16
  ba:  64 fd         sbrc  r22, 4
17
  bc:  96 5f         subi  r25, 0xF6  ; 246
18
19
  // Ones (second byte low nibble)
20
  if (second & 0x08) outl += 8;
21
  be:  63 fd         sbrc  r22, 3
22
  c0:  98 5f         subi  r25, 0xF8  ; 248
23
  if (second & 0x04) outl += 2; //  \_ swapped
24
  c2:  62 fd         sbrc  r22, 2
25
  c4:  9e 5f         subi  r25, 0xFE  ; 254
26
  if (second & 0x02) outl += 4; //  /
27
  c6:  61 fd         sbrc  r22, 1
28
  c8:  9c 5f         subi  r25, 0xFC  ; 252
29
  if (second & 0x01) outl += 1;
30
  ca:  60 fd         sbrc  r22, 0
31
  cc:  9f 5f         subi  r25, 0xFF  ; 255
32
33
  // Hundreds (first byte low nibble)
34
  // 100 still fits in the 8-bit outl variable, so we
35
  // add it here, not below
36
  if (first & 0x01) outl += 100;
37
  ce:  20 fd         sbrc  r18, 0
38
  d0:  9c 59         subi  r25, 0x9C  ; 156
39
40
  // Remaining values don't fit in the 8-but outl, so
41
  // convert to 16 bit for the remaining values
42
  uint16_t out = outl;
43
  d2:  89 2f         mov  r24, r25
44
  d4:  90 e0         ldi  r25, 0x00  ; 0
45
46
  if (first & 0x08) out += 800;
47
  d6:  23 ff         sbrs  r18, 3
48
  d8:  02 c0         rjmp  .+4        ; 0xde <bcd3+0x38>
49
  da:  80 5e         subi  r24, 0xE0  ; 224
50
  dc:  9c 4f         sbci  r25, 0xFC  ; 252
51
  if (first & 0x04) out += 200; //  \_ swapped
52
  de:  22 ff         sbrs  r18, 2
53
  e0:  02 c0         rjmp  .+4        ; 0xe6 <bcd3+0x40>
54
  e2:  88 53         subi  r24, 0x38  ; 56
55
  e4:  9f 4f         sbci  r25, 0xFF  ; 255
56
  if (first & 0x02) out += 400; //  /
57
  e6:  21 ff         sbrs  r18, 1
58
  e8:  02 c0         rjmp  .+4        ; 0xee <bcd3+0x48>
59
  ea:  80 57         subi  r24, 0x70  ; 112
60
  ec:  9e 4f         sbci  r25, 0xFE  ; 254
61
62
  return out;
63
}
64
  ee:  08 95         ret
65
66
000000f0 <main>:
67
68
int main()
69
{
70
  result =  bcd3(firstbyte, secondbyte);
71
  f0:  60 91 00 01   lds  r22, 0x0100  ; 0x800100 <__data_start>
72
  f4:  80 91 01 01   lds  r24, 0x0101  ; 0x800101 <firstbyte>
73
  f8:  0e 94 53 00   call  0xa6  ; 0xa6 <bcd3>
74
  fc:  90 93 03 01   sts  0x0103, r25  ; 0x800103 <__data_end+0x1>
75
 100:  80 93 02 01   sts  0x0102, r24  ; 0x800102 <__data_end>
76
 104:  ff cf         rjmp  .-2        ; 0x104 <main+0x14>



Und das ist der Assemblercode von Pedas 1-Short-Variante:
1
uint16_t bcd3(uint16_t in)
2
{
3
  uint8_t outl = 0;
4
  b0:  90 e0         ldi  r25, 0x00  ; 0
5
  if (in & 0x0001) outl += 100;
6
  if (in & 0x1000) outl += 10;
7
  b2:  34 fd         sbrc  r19, 4
8
  b4:  96 5f         subi  r25, 0xF6  ; 246
9
  if (in & 0x2000) outl += 40;
10
  b6:  35 fd         sbrc  r19, 5
11
  b8:  98 5d         subi  r25, 0xD8  ; 216
12
  if (in & 0x4000) outl += 20;
13
  ba:  36 fd         sbrc  r19, 6
14
  bc:  9c 5e         subi  r25, 0xEC  ; 236
15
  if (in & 0x8000) outl += 80;
16
  be:  33 23         and  r19, r19
17
  c0:  0c f4         brge  .+2        ; 0xc4 <bcd3+0x1e>
18
  c2:  90 5b         subi  r25, 0xB0  ; 176
19
  if (in & 0x0100) outl += 1;
20
  c4:  30 fd         sbrc  r19, 0
21
  c6:  9f 5f         subi  r25, 0xFF  ; 255
22
  if (in & 0x0200) outl += 4;
23
  c8:  31 fd         sbrc  r19, 1
24
  ca:  9c 5f         subi  r25, 0xFC  ; 252
25
  if (in & 0x0400) outl += 2;
26
  cc:  32 fd         sbrc  r19, 2
27
  ce:  9e 5f         subi  r25, 0xFE  ; 254
28
  if (in & 0x0800) outl += 8;
29
  d0:  33 fd         sbrc  r19, 3
30
  d2:  98 5f         subi  r25, 0xF8  ; 248
31
  uint16_t out = outl;
32
  d4:  89 2f         mov  r24, r25
33
  d6:  90 e0         ldi  r25, 0x00  ; 0
34
  if (in & 0x0002) out += 400;
35
  d8:  21 ff         sbrs  r18, 1
36
  da:  02 c0         rjmp  .+4        ; 0xe0 <bcd3+0x3a>
37
  dc:  80 57         subi  r24, 0x70  ; 112
38
  de:  9e 4f         sbci  r25, 0xFE  ; 254
39
  if (in & 0x0004) out += 200;
40
  e0:  22 ff         sbrs  r18, 2
41
  e2:  02 c0         rjmp  .+4        ; 0xe8 <bcd3+0x42>
42
  e4:  88 53         subi  r24, 0x38  ; 56
43
  e6:  9f 4f         sbci  r25, 0xFF  ; 255
44
  if (in & 0x0008) out += 800;
45
  e8:  23 ff         sbrs  r18, 3
46
  ea:  02 c0         rjmp  .+4        ; 0xf0 <bcd3+0x4a>
47
  ec:  80 5e         subi  r24, 0xE0  ; 224
48
  ee:  9c 4f         sbci  r25, 0xFC  ; 252
49
  return out;
50
}
51
  f0:  08 95         ret
52
53
000000f2 <main>:
54
55
int main()
56
{
57
  result =  bcd3((uint16_t)secondbyte<<8 + firstbyte);
58
  f2:  20 91 00 01   lds  r18, 0x0100  ; 0x800100 <__data_start>
59
  f6:  30 e0         ldi  r19, 0x00  ; 0
60
  f8:  90 91 01 01   lds  r25, 0x0101  ; 0x800101 <firstbyte>
61
  fc:  98 5f         subi  r25, 0xF8  ; 248
62
  fe:  a9 01         movw  r20, r18
63
 100:  02 c0         rjmp  .+4        ; 0x106 <main+0x14>
64
 102:  44 0f         add  r20, r20
65
 104:  55 1f         adc  r21, r21
66
 106:  9a 95         dec  r25
67
 108:  e2 f7         brpl  .-8        ; 0x102 <main+0x10>
68
 10a:  ca 01         movw  r24, r20
69
 10c:  0e 94 53 00   call  0xa6  ; 0xa6 <bcd3>
70
 110:  90 93 03 01   sts  0x0103, r25  ; 0x800103 <__data_end+0x1>
71
 114:  80 93 02 01   sts  0x0102, r24  ; 0x800102 <__data_end>
72
 118:  ff cf         rjmp  .-2        ; 0x118 <main+0x26>

Man sieht, dass die Funktion selber exakt gleich lang ist, und der 
Aufwand nur am Zusammenbasteln des Aufruf-Parameters für bcd3() 
entsteht.

Lutz S. schrieb:
> Wenn man das gleich am Anfang macht kann auch die Addition noch
> entfallen.
Was wie macht?

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Lothar M. schrieb:
> Marten Morten schrieb:
>> Ohne es ausgemessen zu haben
> Program Memory Usage   :  268 bytes

Ist das das text-segment? Dann solltest Du auch den µC-Typ angeben.

von MaWin (Gast)


Lesenswert?

Eure Betrachtungen scheinen mir akademisch,
egal wie schnell nud mit wie vielen Befehlsbytes,
denn sie liefern ALLE das falsche Ergebnis,

Laut

NegativeOne! schrieb:
> Und dazu auch noch negiert werden, denn was in den Ergebnis-Bytes eine 1
> sein muss, kommt als 0 aus dem Schieberegister.

aber wir sehen hier:
1
uint16_t bcd3(uint8_t first, uint8_t second)
2
{
3
  uint8_t outl = 0;
4
  if (second & 0x80) outl += 80;

Wenn das bit in second GESETZT ist, wird auch das bit im Ergebniswert 
gesetzt.

Es müsste
1
  uint8_t outl = 0;
2
  if (!(second & 0x80)) outl += 80;

lauten.
Der tabellengesteuerte code braucht natürlich nur andere Tabellenwerte.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Hier eine Variante, die nochmals 2-4 Bytes Code einspart:
1
#include <stdint.h>
2
3
uint8_t firstbyte;
4
uint8_t secondbyte;
5
uint16_t result;
6
7
static uint16_t bcdnibble (uint8_t nibble, uint8_t factor)
8
{
9
  uint16_t r;
10
  r = (nibble & 0x09);              // same as (nibble & 0x08) + (nibble & 0x01)
11
  if (nibble & 0x02) r += 4;        // exchange bits
12
  if (nibble & 0x04) r += 2;        // exchange bits
13
  return r * factor;
14
}
15
16
uint16_t bcd3(uint8_t firstbyte, uint8_t secondbyte)
17
{
18
  uint16_t r;
19
  r = bcdnibble (secondbyte, 1) +
20
      bcdnibble (secondbyte >> 4, 10) +
21
      bcdnibble (firstbyte, 100);
22
  return r;
23
}
24
25
int main()
26
{
27
  result =  bcd3(firstbyte, secondbyte);
28
29
  while(1);
30
}

Der Witz ist die Reduzierung auf Nibbles, wo man dann mit
1
r = (nibble & 0x09)
direkt 2 if-Statements und eine zusätzliche Addition vermeidet.
Aber ob das leserlicher ist?

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Worum geht es denn: Ausführungszeit, Flash-Size, ggf. RAM-Size,
>> Wartbarkeit, ...?
>
> Diese Variante ist in allen 4 Punkten optimal.

Sicher nicht.

Die schnellste Variante ist die, die eine 8KB-LUT verwendet. Und die 
kann man sich mit meinem oben gezeigten Code ganz simpel erstellen 
lassen (zur Compilezeit).

von Marten Morten (Gast)


Lesenswert?

Lothar M. schrieb:
> Lutz S. schrieb:
>> Wenn man das gleich am Anfang macht kann auch die Addition noch
>> entfallen.
> Was wie macht?

Wahrscheinlich meint er
1
uint8_t outl = 0;
2
if (second & 0x01) outl += 1;
an den Anfang zu setzen und dann durch
1
uint8_t outl = second & 0x01;
zu ersetzen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Die schnellste Variante ist die, die eine 8KB-LUT verwendet.

Was glaubst Du, wieviele Millionen mal der TO die Schiebeschalter pro 
Sekunde auslesen möchte?

von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Wilhelm M. schrieb:
>> Die schnellste Variante ist die, die eine 8KB-LUT verwendet.
>
> Was glaubst Du, wieviele Millionen mal der TO die Schiebeschalter pro
> Sekunde auslesen möchte?

Steht hier nicht zur Debatte: es es ging um die u.g. Pauschalaussage, 
und die ist nun mal falsch.

Wilhelm M. schrieb:
> Peter D. schrieb:
>> Wilhelm M. schrieb:
>>> Worum geht es denn: Ausführungszeit, Flash-Size, ggf. RAM-Size,
>>> Wartbarkeit, ...?
>>
>> Diese Variante ist in allen 4 Punkten optimal.

von H.Joachim S. (crazyhorse)


Lesenswert?

MaWin schrieb:
> Eure Betrachtungen scheinen mir akademisch,
> egal wie schnell nud mit wie vielen Befehlsbytes,
> denn sie liefern ALLE das falsche Ergebnis,

Akademisch ist es schon lange - aber trotzdem interessant, wieviel 
Optimierungspotential selbst in einfachen Sachen steckt.

Und falsch ist es auch nicht  - es herrschte Übereinkunft, dass vorher 
negiert wurde bzw. das es vorrangi um die Sortierei geht. Natürlich kann 
man das weglassen und die Bits auf 0 testen bzw. dies bei einer 
LUT-Lösung direkt berücksichtigen.

von Wilhelm M. (wimalopaan)


Lesenswert?

H.Joachim S. schrieb:
> Akademisch ist es schon lange - aber trotzdem interessant, wieviel
> Optimierungspotential selbst in einfachen Sachen steckt.

Wer das interessant findet, sollte sich mal die Diskussion um 
std::midpoint() ansehen.

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Steht hier nicht zur Debatte: es es ging um die u.g. Pauschalaussage,
> und die ist nun mal falsch.

Wenn man es unbedingt falsch verstehen will, dann ja.
Gemeint ist natürlich, daß sie in der Summe aller Faktoren optimal ist.
Klar kann man einzelne Faktoren noch weiter optimieren, wenn man die 
anderen ins Unterirdische absenkt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Gemeint ist natürlich, daß sie in der Summe aller Faktoren optimal ist.

Da legst Du dann Deine, persönliche Gewichtung zugrunde? Na prima, dass 
ist ja Politikergeschwafel!

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Marten Morten schrieb:
> Wahrscheinlich meint er
> uint8_t outl = 0;
> if (second & 0x01) outl += 1;
> an den Anfang zu setzen und dann durch
> uint8_t outl = second & 0x01;
> zu ersetzen.
Stimmt, spart nochmal 4 Byte:
1
uint16_t bcd3(uint8_t first, uint8_t second)
2
{
3
  // perform as many operations as possible with 8-bit
4
  uint8_t outl = second & 0x01;
5
  
6
  // Tenth (second byte high nibble)
7
  if (second & 0x80) outl += 80;
8
  if (second & 0x40) outl += 20; //  \_ swapped
9
  if (second & 0x20) outl += 40; //  /
10
  if (second & 0x10) outl += 10;
11
12
  // Ones (second byte low nibble)
13
  if (second & 0x08) outl += 8;
14
  if (second & 0x04) outl += 2; //  \_ swapped
15
  if (second & 0x02) outl += 4; //  /
16
17
18
  // Hundreds (first byte low nibble)
19
  // 100 still fits in the 8-bit outl variable, so we
20
  // add it here, not below
21
  if (first & 0x01) outl += 100;
22
23
  // Remaining values don't fit in the 8-but outl, so
24
  // convert to 16 bit for the remaining values
25
  uint16_t out = outl;
26
27
  if (first & 0x08) out += 800;
28
  if (first & 0x04) out += 200; //  \_ swapped
29
  if (first & 0x02) out += 400; //  /
30
31
  return out;
32
}
33
34
int main()
35
{
36
  result =  bcd3(firstbyte, secondbyte);
37
38
  while(1);
39
}
Program Memory Usage   :  264 bytes


Frank M. schrieb:
> Hier eine Variante, die nochmals 2-4 Bytes Code einspart
Program Memory Usage   :  288 bytes
Mehraufwand durch die zusätzlichen Funktionsaufrufe...

Jetzt habe ich aber genug. Inzwischen sind gut 15 unterschiedliche 
Varianten zusammengekommen ;-)

: Bearbeitet durch Moderator
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Da legst Du dann Deine, persönliche Gewichtung zugrunde?

Naja, ganze 8 KB für eine Lookup-Table ist jetzt auch nicht der Burner 
gegenüber schlanken 72 Bytes an Code. Schon bei einem ATmega88 wäre hier 
Schicht im Schacht.

Mach doch mal eine Tabelle mit Gewichtungsfaktoren für Speicherbedarf, 
Geschwindigkeit usw.

Dann kann man die vorgestellten Lösungen mit dem Taschenrechner 
"ranken". ;-)

Beispiel: Wären alle Gewichtungsfaktoren gleich hoch, muss Dein Programm 
bei 113 mal höherem Speicherbedarf auch 113 mal schneller sein, um noch 
mitzuhalten.

P.S.
1
8192 / 72 = 113. Deinen Speicherbedarf für den Code habe ich hier noch nicht mitgerechnet.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Naja, ganze 8 KB für eine Lookup-Table ist jetzt auch nicht der Burner
> gegenüber schlanken 72 Bytes an Code. Schon bei einem ATmega88 wäre hier
> Schicht im Schacht.

Der Code ist linear und 40 Bytes (s.a. Geschwindigkeit)

Natürlich ist es klar, das man das bei den Schaltern wohl nicht so 
macht, oder?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Wären alle Gewichtungsfaktoren gleich hoch, muss Dein Programm bei 113
> mal höherem Speicherbedarf auch 113 mal schneller sein, um noch
> mitzuhalten.
Wenn denn sonst kein anderer Parameter wie z.B. die 
Wartbarkeit/Verständlichkeit auch noch beeinflusst werden könnte. Ich 
zumindest möchte keinen Fehler in einer 8kByte großen LUT suchen...  ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Lothar M. schrieb:
> Wenn denn sonst kein anderer Parameter wie z.B. die
> Wartbarkeit/Verständlichkeit auch noch beeinflusst werden könnte. Ich
> zumindest möchte keinen Fehler in einer 8kByte großen LUT suchen...  ;-)

Den brauchst Du auch nicht zu suchen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Natürlich ist es klar, das man das bei den Schaltern wohl nicht so
> macht, oder?

Natürlich ist das klar.

Und wenn es keine Schalter, sondern sonstwie schnelle Signale wären, wo 
man in Zeitnot geraten könnte:

Dann würde man so einen Fehler wie eine Vertauschung von 2 Pins partout 
vermeiden. Und schon wäre die BCD-Wandlung ohne LUT nochmal um ein 
Vielfaches schneller, wodurch der Geschwindigkeitsabstand zur Lösung mit 
LUT auf jeden Fall geringer würde.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Frank M. schrieb:
>> Hier eine Variante, die nochmals 2-4 Bytes Code einspart
> Program Memory Usage   :  288 bytes

Hm, mein avr-gcc 4.7.2 klumpt das zusammen auf 276 bytes. Aber ich weiß 
jetzt auch nicht mehr, was der bisher minimalste Wert war.

> Mehraufwand durch die zusätzlichen Funktionsaufrufe...

Ja, kann sein. Ich dachte, die würden inline durchgehen...

Aber egal, mir reichts jetzt auch. :-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Hm, mein avr-gcc 4.7.2 klumpt das zusammen auf 276 bytes. Aber ich weiß
> jetzt auch nicht mehr, was der bisher minimalste Wert war.

Na dann sag doch endlich mal für welchen Controller! Sonst kann man 
nicht viel vergleichen. Oder gibt die tatsächliche Größe des Codes an, 
ohne vector-table, etc.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Na dann sag doch endlich mal für welchen Controller!

Upps, der war für Atmega644. Ich habe es jetzt mal auf Atmega88 
umgestellt:

Nun sind es nur noch 202 Bytes statt 276 Bytes Programmcode.

> Oder gibt die tatsächliche Größe des Codes an,

Blick in die lss-Datei:

bcdnibble: 36 Bytes
bcd3: 56 Bytes

Macht insgesamt 92 Bytes. Hm, da klingen Pedas 72 Bytes doch besser. 
Aber auf jeden Fall besser als 8232 Bytes.. ;-)

P.S.
Ich frage mich, was Du da vergleichen willst:

1. Dein Code versagt für ATmega88
2. Dein Code versagt mit avr-gcc 4.7.2

: Bearbeitet durch Moderator
von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> leo schrieb:
>> Wilhelm M. schrieb:
>>> template<uint8_t Bit1, uint8_t Bit2, typename T>
>>
>> und weitere ~30 Zeilen.
>>
>> Wie immer die laengste, komlizierteste, unleserlichste und unnoetigste
>> "Loesung" weit und breit ;-)
>
> Naja, swapBits<> steckt man einmal in seinem Leben irgendwo in eine
> Headerdatei. Fertig.

Man kann das natürlich so machen, wenn man Spaß daran hat.

Aber mal Hand aufs Herz:

Hast du die Funktion swapBits schon jemals früher gebraucht?

Oder siehst du eine reelle Chance, sie jemals wiederzuverwenden?

Dazu ist die Funktion trotz Vertemplatisierung zu speziell, denn beim
nächsten ähnlich liegenden Fall wird der Hardwareentwickler sicher nicht
2, sondern 3 oder gar alle 4 Bits durcheinanderschmeißen oder seinen
Fehler einsehen und alle Bits in ihrer natürlichen Reihenfolge halten.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Man kann das noch weiter optimieren, indem man zuerst das Low-Byte
> berechnet:
>
1
> uint16_t bcd3(uint16_t in)
2
> {
3
>   uint8_t outl = 0;
4
>   if (in & 0x0001) outl += 100;
5
...
6
>   return out;
7
> }
8
>
>
> Sind auf dem AVR 72 Byte Code.

Nimmt man das Lesen zweier volatile (Ports) mit hinzu, so kommt man auch 
hier auf 88 Bytes Code, gegenüber 90 Bytes bei der anderen if-Variante. 
Gleiche 90-Bytes erzielt man (m.E. lesbarer und flexibler) mit 
generischem C++, oder 40-Bytes mit 8K-Monster-LUT oder 66-Bytes mit 
(256+32)-Bytes-LUT oder 82-Bytes mit 16-Bytes-LUT.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> leo schrieb:
>>> Wilhelm M. schrieb:
>>>> template<uint8_t Bit1, uint8_t Bit2, typename T>
>>>
>>> und weitere ~30 Zeilen.
>>>
>>> Wie immer die laengste, komlizierteste, unleserlichste und unnoetigste
>>> "Loesung" weit und breit ;-)
>>
>> Naja, swapBits<> steckt man einmal in seinem Leben irgendwo in eine
>> Headerdatei. Fertig.
>
> Man kann das natürlich so machen, wenn man Spaß daran hat.
>
> Aber mal Hand aufs Herz:
>
> Hast du die Funktion swapBits schon jemals früher gebraucht?

Ja.

> Oder siehst du eine reelle Chance, sie jemals wiederzuverwenden?

Ja.

Ich habe eine Klasse für ein PinSet (beliebige Pins eines Ports in 
beliebiger Reihenfolge mit beliebiger LUT). Wenn es nicht sehr auf 
Laufzeit ankommt, kann man sich dadurch ggf. auch das HW-Design 
vereinfachen - also nicht versehentlich, sondern absichtlich 
vertauschen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
>> Hast du die Funktion swapBits schon jemals früher gebraucht?
>
> Ja.
>
>> Oder siehst du eine reelle Chance, sie jemals wiederzuverwenden?
>
> Ja.

Das soll einer verstehen: Du benutzt ständig diese sehr spezifische
swapBits-Funktion (ich persönlich hätte dafür noch nie Bedarf gehabt),
obwohl du eine weitaus generischere Funktion in der Hinterhand hast:

> Ich habe eine Klasse für ein PinSet (beliebige Pins eines Ports in
> beliebiger Reihenfolge mit beliebiger LUT).

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>>> Hast du die Funktion swapBits schon jemals früher gebraucht?
>>
>> Ja.
>>
>>> Oder siehst du eine reelle Chance, sie jemals wiederzuverwenden?
>>
>> Ja.
>
> Das soll einer verstehen:

Was ist daran schwer (s.u.)?

> Du benutzt ständig diese sehr spezifische
> swapBits-Funktion (ich persönlich hätte dafür noch nie Bedarf gehabt),
> obwohl du eine weitaus generischere Funktion in der Hinterhand hast:

Nein. Die Funktion ist Teil der genannten Klasse.

Die Klasse zu posten wäre möglich, aber sinnlos.

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> Natürlich ist es klar, das man das bei den Schaltern
> wohl nicht so macht, oder?

Ich dachte, du hast deine Lösung genau deswegen gepostet?

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
Noch kein Account? Hier anmelden.