2009-08-26 4 views
4

Dies ist ein Ausschnitt des disassemblierten AVR-Codes von einem C-Projekt, an dem ich gerade arbeite. Ich habe bemerkt, dass dieser seltsame Code erzeugt wurde, und ich kann nicht verstehen, wie er funktioniert. Ich nehme an, es ist eine Art lächerliche Optimierung ...Warum wird dieser Code von avr-gcc erzeugt und wie funktioniert es?

Was ist die Erklärung?

92:   ticks++;   // unsigned char ticks; 
+0000009F: 91900104 LDS  R25,0x0104  Load direct from data space 
+000000A1: 5F9F  SUBI  R25,0xFF  Subtract immediate 
+000000A2: 93900104 STS  0x0104,R25  Store direct to data space 
95:   if (ticks == 0) { 
+000000A4: 2399  TST  R25   Test for Zero or Minus 
+000000A5: F009  BREQ  PC+0x02  Branch if equal 
+000000A6: C067  RJMP  PC+0x0068  Relative jump 

Insbesondere warum hat der zweite Befehl subtrahieren 0xFF von R25 statt nur INC R25?

+2

Falls es nicht offensichtlich ist, beziehe ich mich auf die zweite Zeile: Subtrahiere 0xFF von R25 ... warum nicht einfach "INC R25"? –

+1

Welcher C-Datentyp ist Ticks? – Inshallah

+0

unsigned char (8 bits) –

Antwort

4

Die SUBI-Befehl verwendet werden kann, um zu addieren/subtrahieren jedes 8-Bit-Konstante zu/von einem 8-Bit-Wert. Es hat die gleichen Kosten wie INC, d.h. Befehlsgröße und Ausführungszeit. Daher wird SUBI vom Compiler bevorzugt, weil es allgemeiner ist. Es gibt keinen entsprechenden ADDI-Befehl, wahrscheinlich weil er überflüssig wäre.

+1

Sie haben Recht, dass beide die gleiche Größe/CPU-Uhren haben - dennoch sagt "SUBI" ist vorzuziehen (...), weil es allgemeiner ist " nur Handwaving, ohne irgendwelche Besonderheiten zu beschreiben; "SUBI" ist nicht "allgemeiner", weil es die Register r16-31 anstelle des "INC" -R0-31-Bereichs benötigt. Der avr-gcc-Compiler wurde * von Hand eingestellt *, um "SUBI" anstelle von "INC" zu tun, genau aus dem Grund, der von Greg unten angegeben wurde - "SUBI" setzt das Übertragsflag richtig, während "INC" es einfach ignoriert. Es macht 16/32-Bit-Zähler mit 'SUBI' viel einfacher, weil Sie nie niedrigere Bytes auf Überlauf prüfen müssen! – vaxquis

3

tl; dr der Compiler wurde entwickelt, um die portabler, effiziente & allgemeine Lösung hier zu verwenden.

Die SUBI Befehlssätze C (Carry) und H (Halb carry) CPU-Flags für die Verwendung mit nachfolgenden Anweisungen (es gibt keine ADDI in 8-Bit-AVR BTW, so einen unmittelbaren Wert von x fügen wir subtrahieren -x von es), während INC nicht. Da sowohl SUBI 2 Bytes der Länge haben und während 1 Taktzyklus ausgeführt werden, verlieren Sie nichts mit SUBI - OTOH, wenn Sie einen 8-Bit-großen Zähler verwenden, können Sie dann leicht erkennen, ob es gerollt ist (von BRCC/BRCS), und wenn Sie einen 16- oder 32-Bit-Zähler haben, können Sie ihn auf sehr einfache Weise inkrementieren - mit nur INC, 0x00FF würde auf 0x0000 erhöht werden, also müssten Sie Überprüfen Sie, ob das niedrigste Byte 0xFF vor INC ing ist. OTOH, mit SUBI Sie nur SUBI -1 das niedrigste Byte und dann ADC 0 für die folgenden Bytes, die sicherstellen, dass alle möglichen Übertrag Bits berücksichtigt wurde.

Weiterführende Literatur:

https://lists.gnu.org/archive/html/avr-gcc-list/2008-11/msg00029.html

http://avr-gcc-list.nongnu.narkive.com/SMMzdBkW/foo-subi-vs-inc

+0

Ich habe die nächsten paar Anweisungen hinzugefügt ... hilft das, die Dinge einzugrenzen? Ich bekomme immer noch nicht den Punkt "SUBI R25,0xFF" vs. "INC R25", beide sind 1 Takt. Und wenn R25 von 255 auf 0 übergeht, würde der Befehl TST R25 gut funktionieren. –

+0

Ok. Ich dachte mir das etwas durch und erkannte, dass bei nicht vorzeichenbehafteter Mathematik das Subtrahieren von 0xFF dasselbe ist wie das Hinzufügen von 0x01. Sonderbar, aber warum so? Stellt INC keine Flags für den Zweig ein? –

+2

Es sieht so aus, als ob INC die Flags V, N, S, Z setzt, aber SUBI setzt H, V, N, S, Z, C. Da der Compiler als nächstes einen TST-Befehl erzeugt hat, scheint er die Flags aus dem SUBI sowieso nicht zu verwenden. Auch die Verwendung eines SUBI scheint seltsam, weil ein ADDI mit 0x01 wieder äquivalent wäre. Es ist wirklich ein Geheimnis. –