Forum: Compiler & IDEs Inline-Assembler: Label-Sprungadresse in Z-Register laden - wie?


von Simon B. (nomis)


Lesenswert?

Hallo allerseits.

Ich kämpfe gerade mit dem AVR-GCC inline assembler. Ich arbeite an einer 
zeitkritischen IRQ-Routine, die fürs Multiplexen einer LED-Matrix 
zuständig ist. Dabei wird je nach aktiver Spalte ein Portbit gelöscht. 
In C sieht das so aus:
1
  /* enable new column */
2
  if (col < 4) {
3
    PORTD &= ~(1 << (col+4));
4
  } else if (col < 0x0a) {
5
    PORTB &= ~(1 << (col-4+2));
6
  } else {
7
    PORTA &= ~(1 << (col-0x0a));
8
  }

Wenn ich mir den daraus generierten Assembler angucke und anfange, 
Zyklen zu zählen, drängt sich mir der Gedanke auf, dass das schneller 
gehen muss. Meine Idee war etwa folgendes:
1
  __asm__ __volatile__ ( ""
2
    "ldi r31,hi(start%=)"        "\n\t" // "Error: garbage at end of line"
3
    "ldi r30,lo(start%=)"        "\n\t" // "Error: garbage at end of line"
4
    "add r30,%[col]"             "\n\t"
5
    "adc r31,__zero_reg__"       "\n\t"
6
    "add r30,%[col]"             "\n\t"
7
    "adc r31,__zero_reg__"       "\n\t"
8
    "add r30,%[col]"             "\n\t"
9
    "adc r31,__zero_reg__"       "\n\t"
10
    "ijmp"                       "\n"
11
"start%=:"                       "\n\t"
12
    "cbi %[portd], 4"            "\n\t"
13
    "jmp out%="                  "\n\t"
14
    "cbi %[portd], 5"            "\n\t"
15
    "jmp out%="                  "\n\t"
16
[...]
17
    "cbi %[porta], 6"            "\n\t"
18
    "jmp out%="                  "\n\t"
19
    "cbi %[porta], 7"            "\n\t"
20
    "jmp out%="                  "\n"
21
"out%=:"                         "\n\t"
22
23
    :
24
    : [col]   "d" (col),
25
      [porta] "I" (_SFR_IO_ADDR (PORTA)),
26
      [portb] "I" (_SFR_IO_ADDR (PORTB)),
27
      [portd] "I" (_SFR_IO_ADDR (PORTD))
28
  );

Ich will also die Adresse des "start%="-Labels in das Z-Register laden, 
dann darauf dreimal col addieren (cbi: 1 Wort, jmp: 2 Worte) und dann 
mittels ijmp an die resultierende Adresse springen.

Nur kriege ich es nicht hin, die Adresse des Labels in das Z-Register zu 
laden, mir ist die Syntax des Assemblers total unklar. Der Assembler ist 
der Meinung, dass bei meinem obigen Versuch "garbage at end of line" 
steht.

Wie referenziere ich denn auf diese Adresse?

Danke für die Tipps,
        Simon

von Oliver (Gast)


Lesenswert?

Schreib doch die ganze Funktion direkt in Assembler, und lass den 
inline-Quatsch sein. Inline-Assembler im gcc ist nunmal Krampf, und ist 
für mehr als drei zeilen ungeeignet.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Das geth auch schneller.

Wenn man realisiert, das eine Schiebeoperation mit einer variablen 
Bitanzahl eine teure Sache ist.

Mach dir ein Array, in dem du die Ausgabewerte direkt vorliegen hast, 
indiziere mit col und du bist wieder auf full Speed. Ganz ohne 
Assembler.

Edit: natürlich 3 Arrays. Für jeden Port eines

Ob es jetzt einfacher ist einfach alle 3 Ports jeweils komplett neu zu 
bestücken oder vorher mittels if rauszufinden, welcher Port ein Update 
braucht, müsste man sich ansehen. Auch ein if kostet Zeit.


Das was du da gerade mühsam in Assembler zu konstruieren versuchst, ist 
nichts anderes als das was der Compiler bei einem switch/case erzeugen 
würde. Warum kannst du den nicht in C schreiben, wenn du schon keine 
Array Lösung mit vordefinierten auszugebenden Konstanten machen willst?

Alles in allem: erst mal in der Hochsprache das letzte aus dem Code 
rausholen. Da geht meistens noch viel. Assembler ist deine letzte 
Option, wenn du auch noch den letzten Taktzyklus, auf den der Compiler 
nicht geachtet hat, rausholen willst. Und meistens braucht man den auch 
gar nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver schrieb:
> Inline-Assembler im gcc ist nunmal Krampf, und ist
> für mehr als drei zeilen ungeeignet.

Anders: inline-Assembler im GCC ist genial und unwahrscheinlich gut
mit dem Compiler integrierbar, aber das hat seinen Preis.  Der Preis
heißt "constraints", denn die beschreiben dem Compiler, wie er
den Assemblercode mit dem Compilat optimal angepasst bekommt.

Aber ich sehe das auch so, entweder die ganze ISR vollständig in
einer Assemblerdatei hinterlegen, oder eben erstmal schauen, was
man im C-Code noch besser schreiben kann.

Im Übrigen heißen die Operatoren nicht "hi" und "lo", sondern "hi8"
und "lo8".

von Simon B. (nomis)


Lesenswert?

Jörg Wunsch schrieb:
> Im Übrigen heißen die Operatoren nicht "hi" und "lo", sondern "hi8"
> und "lo8".

Äh, guter Punkt. Ich habe das jetzt so realisiert, und es funktioniert:
1
  __asm__ __volatile__ ( ""                   // 11 cycles:
2
    "ldi r30,lo8(pm(start%=))"   "\n\t"       // 1
3
    "ldi r31,hi8(pm(start%=))"   "\n\t"       // 1
4
    "add %[col],%[col]"          "\n\t"       // 1
5
    "add r30,%[col]"             "\n\t"       // 1
6
    "adc r31,__zero_reg__"       "\n\t"       // 1
7
    "ijmp"                       "\n"         // 2
8
  "start%=:"                     "\n\t"
9
    "cbi %[portd], 4"            "\n\t"
10
    "rjmp out%="                 "\n\t"
11
    "cbi %[portd], 5"            "\n\t"
12
    "rjmp out%="                 "\n\t"
13
[...]
14
    "cbi %[porta], 6"            "\n\t"       // 2
15
    "rjmp out%="                 "\n\t"       // 2
16
    "cbi %[porta], 7"            "\n"
17
  "out%=:"                       "\n\t"
18
    :
19
    : [col]   "d" (col),
20
      [porta] "I" (_SFR_IO_ADDR (PORTA)),
21
      [portb] "I" (_SFR_IO_ADDR (PORTB)),
22
      [portd] "I" (_SFR_IO_ADDR (PORTD))
23
    : "r30", "r31"
24
  );

Das sind jetzt 11 Zyklen (bzw. 10 für 18/PA7). Ich habe so meine 
Zweifel, dass man das mit einer Lookup-Table schneller hinbekomt. Das C 
ist sicherlich noch nicht ausgereizt, aber die switch()-Anweisung die 
ich zwischendrin ausprobiert hat, hat irgendwie gruslig viel Kram 
erzeugt. Der Compiler erkennt vermutlich nicht, dass alle case-Fälle 
gleich viel Code
enthalten und daher Arithmetik mit dem switch-Argument gemacht werden 
kann.

> Aber ich sehe das auch so, entweder die ganze ISR vollständig in
> einer Assemblerdatei hinterlegen, oder eben erstmal schauen, was
> man im C-Code noch besser schreiben kann.

Ich werde mir die anderen Punkte in der ISR definitiv noch angucken und 
ggf. nach Assembler portieren. Alles komplett in asm ist definitiv 
interessant, schon alleine um die Menge der verwendeten Register zu 
minimieren und den push/pop-Overhead zu reduzieren.

Vielen Dank für die Hinweise.
        Simon

von Simon B. (nomis)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das was du da gerade mühsam in Assembler zu konstruieren versuchst, ist
> nichts anderes als das was der Compiler bei einem switch/case erzeugen
> würde. Warum kannst du den nicht in C schreiben, wenn du schon keine
> Array Lösung mit vordefinierten auszugebenden Konstanten machen willst?

Hm, ok. Der Assembler den er jetzt mit einem Switch generiert sieht 
schon besser aus, als das was ich da gestern abend gemeint habe zu 
lesen. Keine Ahnung, ob das an Leseinkompetenz auf meiner Seite, oder an 
unterschiedlichen Compilerflags liegt.

Folgender C-Code
1
  switch (col) {
2
    case  0: PORTD &= ~(1 << 4) ; break;
3
    case  1: PORTD &= ~(1 << 5) ; break;
4
    case  2: PORTD &= ~(1 << 6) ; break;
5
[...]
6
    case 15: PORTA &= ~(1 << 5) ; break;
7
    case 16: PORTA &= ~(1 << 6) ; break;
8
    case 17: PORTA &= ~(1 << 7) ; break;
9
  }

wird zu diesem Assembler übersetzt:
1
.LM17:
2
        movw r30,r20
3
        cpi r30,18
4
        cpc r31,__zero_reg__
5
        brsh .L8
6
        subi r30,lo8(-(gs(.L27)))
7
        sbci r31,hi8(-(gs(.L27)))
8
        lsl r30
9
        rol r31
10
        lpm __tmp_reg__,Z+
11
        lpm r31,Z
12
        mov r30,__tmp_reg__
13
        ijmp
14
        .data
15
        .section .progmem.gcc_sw_table, "a", @progbits
16
        .p2align 1
17
.L27:
18
        .data
19
        .section .progmem.gcc_sw_table, "a", @progbits
20
        .p2align 1
21
        .word gs(.L9)
22
        .word gs(.L10)
23
        .word gs(.L11)
24
[...]
25
        .word gs(.L24)
26
        .word gs(.L25)
27
        .word gs(.L26)
28
        .section        .text.__vector_16
29
.L26:
30
        .stabn  68,0,188,.LM18-.LFBB2
31
.LM18:
32
        cbi 34-32,7
33
.L8:
34
        .stabn  68,0,254,.LM19-.LFBB2
35
    // Hier folgt dann der Rest des IRQ-Handlers
36
37
[...]
38
// Beispielhaft ein paar der Sprungziele
39
.L15:
40
        .stabn  68,0,177,.LM37-.LFBB2
41
.LM37:
42
        cbi 37-32,4
43
        rjmp .L8
44
.L10:
45
        .stabn  68,0,172,.LM38-.LFBB2
46
.LM38:
47
        cbi 43-32,5
48
        rjmp .L8
49
.L11:
50
        .stabn  68,0,173,.LM39-.LFBB2
51
.LM39:
52
        cbi 43-32,6
53
        rjmp .L8
54
.L9:
55
        .stabn  68,0,171,.LM40-.LFBB2
56
.LM40:
57
        cbi 43-32,4
58
        rjmp .L8
59
[...]
d.h. man hat hier die Indirektionstabelle, die aus dem Program-Memory 
geladen wird. Ok, nur eine Handvoll Zyklen mehr, aber an denen knabbere 
ich halt gerade...

> Alles in allem: erst mal in der Hochsprache das letzte aus dem Code
> rausholen. Da geht meistens noch viel. Assembler ist deine letzte
> Option, wenn du auch noch den letzten Taktzyklus, auf den der Compiler
> nicht geachtet hat, rausholen willst. Und meistens braucht man den auch
> gar nicht.

Ich würde es nicht tun, wenn ich nicht das Gefühl hätte, an die Grenzen 
zu stoßen. Die C-Implementation funktioniert gut, man sieht auf der 
Matrix (18x8) allerdings durchaus noch ein Flimmern, dass ich gerne 
durch eine Erhöhung der Framerate loswürde.

Im Moment erreiche ich bei 12MHz eine Bildwiederholfrequenz von ca. 
168Hz, die kürzeste Zeitscheibe für mein Timerinterrupt (mit variablen 
Zeitintervallen für 5-bit-Graustufen) ist dann 128 Zyklen lang. Das soll 
weniger werden. Wenn ich es z.B. schaffen würde, auf 64 Zyklen 
runterzukommen hätte ich ruck-zuck die doppelte Bildwiederholfrequenz.

Deswegen suche ich gerade intensiv nach überflüssigen Zyklen...  :)

Viele Grüße,
        Simon

von holger (Gast)


Lesenswert?

Wieviele Zyklen ergibt das?
1
const uint8_t dataporta[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
2
const uint8_t dataportb[] = { 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
3
const uint8_t dataportd[] = { 0x10, 0x20, 0x40, 0x80};
4
5
void colreg(uint8_t col)
6
{
7
8
 switch(col)
9
 {
10
  case 0:
11
  case 1:
12
  case 2:
13
  case 3:
14
    PORTD &= ~dataportd[col];
15
    break;
16
  case 4:
17
  case 5:
18
  case 6:
19
  case 7:
20
  case 8:
21
  case 9:
22
    col = col - 4;
23
    PORTB &= ~dataportb[col];
24
    break;
25
  case 10:
26
  case 11:
27
  case 12:
28
  case 13:
29
  case 14:
30
  case 15:
31
  case 16:
32
  case 17:
33
    col = col - 10;
34
    PORTA &= ~dataporta[col];
35
    break;
36
 }
37
}

von Karl H. (kbuchegg)


Lesenswert?

und alternativ schwebt mir noch das vor
1
const uint8_t dataporta[] = ...
2
const uint8_t dataportb[] = ...
3
const uint8_t dataportd[] = ...
4
5
void colreg(uint8_t col)
6
{
7
  PORTD &= ~dataportd[col];
8
  PORTB &= ~dataportb[col];
9
  PORTA &= ~dataporta[col];
10
}
wobei die Arrays so aufgeblasen werden, dass man keinen Zugriffsschutz 
und keine Arithmetik an col braucht. Die paar Bytes zusätzlich tun nicht 
weh

Das müsste dann sein
1
const uint8_t dataporta[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
2
const uint8_t dataportb[] = { 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
3
const uint8_t dataportd[] = { 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

Aber lieber nochmal kontrollieren, ob ich mich da irgendwo verfranst 
habe :-)

von Karl H. (kbuchegg)


Lesenswert?

> man sieht auf der Matrix (18x8) allerdings durchaus noch ein Flimmern,
> dass ich gerne durch eine Erhöhung der Framerate loswürde.

Bei 168Hz siehst du noch ein Flimmern?
Mit dir möchte ich nicht ins Kino gehen. Was siehst du dort? Eine 
Abfolge von Standbildern?

von holger (Gast)


Lesenswert?

So sieht es noch besser aus. Jedes rechnen vermeiden;)
1
const uint8_t dataporta[] = { 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F};
2
const uint8_t dataportb[] = { 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F};
3
const uint8_t dataportd[] = { 0xEF, 0xDF, 0xBF, 0x7F};
4
5
void colreg(uint8_t col)
6
{
7
 switch(col)
8
 {
9
  case 0:
10
  case 1:
11
  case 2:
12
  case 3:
13
    PORTD &= dataportd[col];
14
    break;
15
  case 4:
16
  case 5:
17
  case 6:
18
  case 7:
19
  case 8:
20
  case 9:
21
    col = col - 4;
22
    PORTB &= dataportb[col];
23
    break;
24
  case 10:
25
  case 11:
26
  case 12:
27
  case 13:
28
  case 14:
29
  case 15:
30
  case 16:
31
  case 17:
32
    col = col - 10;
33
    PORTA &= dataporta[col];
34
    break;
35
 }
36
}

Dann noch den Vorschlag von Karl Heinz dazu.

von Simon B. (nomis)


Lesenswert?

Hm, ich sehe schon - ich muss nachher mal ein großes Code-Shootout 
veranstalten... Vielen Dank für die vielen Vorschläge   :-)

Karl Heinz Buchegger schrieb:
>> man sieht auf der Matrix (18x8) allerdings durchaus noch ein Flimmern,
>> dass ich gerne durch eine Erhöhung der Framerate loswürde.
>
> Bei 168Hz siehst du noch ein Flimmern?
> Mit dir möchte ich nicht ins Kino gehen. Was siehst du dort? Eine
> Abfolge von Standbildern?

Ja, das habe ich mich auch schon gefragt...   :-)

Wenn man ruhig auf die Matrix draufguckt, ist das auch kein Problem. Es 
wird dann problematisch, wenn die Platine (gar nicht mal so) schnell 
durch die Gegend geschwenkt wird, oder man die Platine mit einer 
Augenbewegung überstreift. Dann löst sich das Bild in ein komisches 
Punktmuster auf.

Im Gegensatz zum Kino habe ich halt auch bei 100% Helligkeit nur einen 
Duty-Cycle von 1/18, möglicherweise trägt das dazu bei, k. A.

Viele Grüße,
        Simon

von Karl H. (kbuchegg)


Lesenswert?

Simon Budig schrieb:

> Wenn man ruhig auf die Matrix draufguckt, ist das auch kein Problem. Es
> wird dann problematisch, wenn die Platine (gar nicht mal so) schnell
> durch die Gegend geschwenkt wird,
> oder man die Platine mit einer
> Augenbewegung überstreift. Dann löst sich das Bild in ein komisches
> Punktmuster auf.

Schon.
Aber 168Hz ist schon reichlich viel

>
> Im Gegensatz zum Kino habe ich halt auch bei 100% Helligkeit nur einen
> Duty-Cycle von 1/18, möglicherweise trägt das dazu bei, k. A.

Du hast aber schon die 1/18 in die 168Hz eingerechnet (genauso wie deine 
Graustufen). D.h. du kriegst in 1 Sekunde 18*168 = 2688 komplette Bilder 
hin (was an sich schon recht sportlich ist)

Wieso eigentlich 1/18? Hast du das Multiplexen etwa über die Spalten 
gemacht? Man multiplext immer über die kleinere Zahl. Anstelle von 18 
Spalten multiplexen, multiplext man dann eben 8 Zeilen und schon hat man 
einen besseren Duty-Cycle und auch die Framerate geht in die Höhe. 
Alleine diese einfache Änderung verdoppelt dir schon mal die Framerate.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ich denke, das beste ist eine Assembler-Implementierung so wie du sie 
ursprünglich geplant hattest:
1
#define __zero_reg__ r1
2
#define __tmp_reg__ r0
3
4
#include <avr/io.h>
5
6
#define IO(x) _SFR_IO_ADDR(x)
7
8
.macro XSET port, bit
9
    cbi IO(\port), \bit $ rjmp 1f
10
.endm
11
12
.macro DEFUN name
13
.global \name
14
.func \name
15
\name:
16
.endm
17
18
.macro ENDF name
19
.size \name, .-\name
20
.endfunc
21
.endm
22
23
.data
24
.text
25
26
DEFUN TIMER1_COMPA_vect
27
    ;; prolog
28
    push    r31
29
    push    r30
30
    in      r30, IO(SREG)
31
    push    r30
32
    
33
    ;; jump
34
    lds     r30, col
35
    lsl     r30
36
    lsl     r30
37
    clr     r31
38
    subi    r30, lo8(-(pm(0f)))
39
    sbci    r31, hi8(-(pm(0f)))
40
    ijmp
41
42
0:  XSET PORTB, 4
43
    XSET PORTC, 1
44
    XSET PORTD, 2
45
    XSET PORTD, 3
46
    XSET PORTD, 4
47
    XSET PORTD, 5
48
1:
49
    ;; epilog
50
    pop     r30
51
    out     IO(SREG), r30
52
    pop     r30
53
    pop     r31
54
    reti
55
ENDF TIMER2_COMPA_vect

Allein der effektiven Pro/Epilog spart mehr als das Rumgefummel mit 
Tabellen, zB wird so weder __tmp_reg__ noch __zero_reg__ benötigt.

Weitere 2 Ticks können gespart werden, indem col nicht um 1 hochgezählt 
wird in main, sondern um 4 :-) Und IRQs nur möglichst kurz sperren um 
die IRQ-Latenz nicht unnötig zu erhöhen (andere ISRs!).

Den größten Gewinn würde aber ein schnellerer Quarz bringen, zB 24 MHz.

Ich hab meine Scope-Clock mit 24 MHz laufen. Das geht problemlos 
(ATmega168@5V) und reicht sogar um 50000 Pixel/Sekunde auf die Röhre zu 
bekommen und Animation für Asteroids/Snake-Spiel zu berechnen :-)

von Simon B. (nomis)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Du hast aber schon die 1/18 in die 168Hz eingerechnet (genauso wie deine
> Graustufen). D.h. du kriegst in 1 Sekunde 18*168 = 2688 komplette Bilder
> hin (was an sich schon recht sportlich ist)

Nein, es sind 168 Graustufen-Bilder pro Sekunde.

> Wieso eigentlich 1/18? Hast du das Multiplexen etwa über die Spalten
> gemacht? Man multiplext immer über die kleinere Zahl. Anstelle von 18
> Spalten multiplexen, multiplext man dann eben 8 Zeilen und schon hat man
> einen besseren Duty-Cycle und auch die Framerate geht in die Höhe.
> Alleine diese einfache Änderung verdoppelt dir schon mal die Framerate.

Ja ich multiplexe über die Spalten. Ich habe eben mal im 
"Kunstwerke"-Thread die Platine vorgestellt da werden vielleicht ein 
paar Hintergründe klarer: 
Beitrag "Re: Zeigt her Eure Kunstwerke !"

Ich multiplexe über die Spalten, weil ich auf der Platine keinen Platz 
für Treiberbausteine oder Transistoren habe, der Atmel treibt also die 
Low-Current-LEDs direkt. Und es sind nur 8 statt 18 Vorwiderstände, was 
auf meiner Platinengröße echt hilft...  :)

Wenn eine Spalte mit 8 LEDs mit ca. 2mA gleichzeitig leuchtet, dann kann 
der Atmel den Drain-Strom locker verkraften, bei 18*2mA wäre ich schon 
deutlich außerhalb der Spec. Außerdem kommt diese Organisation einem 
schnellen Update sehr entgegen: Die Zeilen hängen an einem Port, um also 
die Daten einer Spalte zu aktualisieren brauche ich nur ein Byte auf den 
PORTC zu schreiben.

Viele Grüße,
        Simon

von Simon B. (nomis)


Lesenswert?

Simon Budig schrieb:
> Hm, ich sehe schon - ich muss nachher mal ein großes Code-Shootout
> veranstalten... Vielen Dank für die vielen Vorschläge   :-)

So, habe ich mal gemacht, irgendwie muss man sich die Nacht ja um die 
Ohren schlagen...   :)

Ich habe vier Ansätze miteinander verglichen:

* switch() zu verschiedenen Einzel-Bit-Befehlen
* switch() mit Tabellenlookup für die drei Ports
* Sprunglose Variante mit drei Tabellen für die Ports
* Sprunglose Variante mit einer Tabelle und einer kleinen Optimierung.

Die erste Variante (switch zu Einzel-Bit-Ops) ist mit 17 Zyklen die 
beste. Es folgt das Switch mit Tabellenlookup (18 Zyklen, evtl. abhängig 
von dem Wert von col).

Die Varianten ohne Sprünge (also einfach stur auf alle Ports schreiben) 
fallen etwas ab: 25 Zyklen für die Variante mit drei Tabellen und 19 
Zyklen für die vereinheitlichte Lookup-Tabelle (und der Optimierung, 
dass man in PORTA einfach so reinschreiben kann, weil der ausschließlich 
Spalten-Bits hat).

Ich habe jetzt nur die Zyklen gezählt, die diese Kern-Funktionalität 
umfassen, insbes. gehe ich von "col" in einem Register aus. 
unterschiedliche Prolog-Größen etc. habe ich nicht betrachtet, aber auch 
da sieht mir die Bit-Operation-switch()-Variante am besten aus (drei 
Register werden gesichert).

Also, sie sind alle dicht beieinander, sind aber doch noch ein Eck von 
dem handoptimierten Assembler weg. Ich werde in der nächsten Zeit dann 
auch nochmal die anderen Sachen in der ISR angucken und sehen, was sich 
da noch rausholen lässt.

Johannes: Danke für die Anregungen, wie man ein eigenständiges 
Assembler-File mit in ein (ansonsten) C-Projekt integriert muss ich mir 
nochmal im Detail angucken, auch mit Makros habe ich noch nix sinnvolles 
gemacht, das sieht aber so aus als könnte das den Code deutlich 
übersichtlicher machen.

(4 ist aber glaube ich der falsche Faktor, 2 müsste richtig sein, da im 
Program-Memory wortweise adressiert wird und XSET zwei Worte umfasst)

Viele Grüße,
        Simon

von Simon B. (nomis)


Angehängte Dateien:

Lesenswert?

Ach, jetzt habe ich dann doch noch die Dateien vergessen. Hier sind sie.

Viele Grüße,
        Simon

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon Budig schrieb:
> wie man ein eigenständiges
> Assembler-File mit in ein (ansonsten) C-Projekt integriert muss ich mir
> nochmal im Detail angucken

http://www.nongnu.org/avr-libc/user-manual/group__asmdemo.html

von Ralf (Gast)


Lesenswert?


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.