Forum: Compiler & IDEs mspgcc inline assembler nested loops


von Gast (Gast)


Lesenswert?

Kann der inline assembler auch nested loops verarbeiten? Das Manual gibt 
zu den Labels nichts her. Eine einfache Schleife ist einfach, jedoch ist 
nirgendwo beschrieben, wie nested Loops auszusehen haben und ob das 
überhaupt geht.

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


Lesenswert?

Du kannst, wie überall im gas (GNU assembler), "local labels" benutzen.
Das sieht ungefähr so aus:
1
1:   something
2
     something else
3
2:   more stuff
4
     goes here
5
     breq 99f
6
     other stuff
7
     brne 2b
8
     even more junk
9
     brne 1b
10
99:

Beim Sprungziel bezeichnet ein angehängtes `b', dass der entsprechende
Label rückwärts zu suchen ist, ein `f', dass die Suche vorwärts
statt findet.   Wenn es Labels mit gleicher Nummer gibt, ,,gewinnt''
der, der in Suchrichtung am nächsten liegt.

von Gast (Gast)


Lesenswert?

Oh danke, die Beispiele, die ich nur gefunden hatte, bezogen sich immer 
nur auf eine Schleife.

von Gast (Gast)


Lesenswert?

Nachtrag. Und wenn man genau hinschaut, da steht in den Beispielen 
tatsächlich 1b und nicht lb. Hier sieht es auch noch gleich aus. Ich 
habe das 1b als Abkürzung für Label gelesen.

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


Lesenswert?

Gast wrote:

> Nachtrag. Und wenn man genau hinschaut, da steht in den Beispielen
> tatsächlich 1b und nicht lb. Hier sieht es auch noch gleich aus. Ich
> habe das 1b als Abkürzung für Label gelesen.

Beitrag "Re: Merkwürdige Fragen (Forum-Trolle?)"

von Gast (Gast)


Lesenswert?

Habe jetzt ein wenig mit dem Syntax rumgespielt. Er ist einfach nur 
furchtbar :-) Aber für eine einfache aber genaue delay-Routine sollte er 
reichen.

Hier der Code, unabhängig davon, was er macht:
1
    void delay_ms(int time, int count)
2
    {
3
        __asm__ __volatile__
4
        (
5
          "1:                           \n\t"
6
          "2:                           \n\t"
7
          " dec %[inner]                \n\t"
8
          " jne 2b                      \n\t"
9
              : [inner] "=r"(time)
10
          " dec %[outer]                \n\t"
11
          " jne 1b                      \n\t"
12
              : [outer] "=r"(count)
13
        );
14
    }

Ich erhalte den Fehler "Syntax error before string constant". Gemeint 
ist damit die Zeile mit " dec %[outer]                \n\t".

Um den grauenvollen Syntax erst einmal zu verstehen, habe ich 
verschiedene Quellen benutzt:
http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc
http://mspgcc.sourceforge.net/manual/c1308.html
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

Abgeleitet war die Loop aus einem Beispiel, wo ein "+r" verwendet wurde. 
Es ist nirgendwo beschrieben, scheint aber zu funktionieren. Egal. Wo 
der Fehler oben liegt, habe ich noch nicht rausgefunden.

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


Lesenswert?

Gast wrote:

> Habe jetzt ein wenig mit dem Syntax rumgespielt. Er ist einfach nur
> furchtbar :-)

Naja, sie ist unwahrscheinlich mächtig, aber das macht sie für einen
Anfänger nicht leicht zu benutzen.  Wenn du sie aber mal mit anderen
Compilern vergleichst, bei denen darfst du oftmals darauf hoffen, dass
der Compiler deine internen Variablen in der nächsten Version noch im
gleichen Register ablegen wird wie in der aktuellen, da sie keine
Möglichkeit haben, derartige Dinge in das inline asm statement hinein
zu propagieren.

GCC benutzt für inline asm letztlich die gleiche Semantik wie für
seine compiler-internen "insns", d. h. es werden die gleichen Wege für
die Register-Allokation benutzt usw.  Der Vorteil ist, dass damit dem
Implementierer der Systembibliothek ein mächtiges Werkzeug in die Hand
gegeben ist, seinen Nutzern für viele Details der Zielplattform
effektive Lösungen anbieten zu können.  Für einen Anfänger sind sie
eher nicht geeignet, und sie können/sollen auch keinesfalls explizite
Assemblerprogramme ablösen.

>         _asm_ __volatile__
>         (
>           "1:                           \n\t"
>           "2:                           \n\t"

Warum zwei Labels auf die gleiche Stelle?

>           " dec %[inner]                \n\t"
>           " jne 2b                      \n\t"
>               : [inner] "=r"(time)
>           " dec %[outer]                \n\t"

Das geht nicht.  Die Syntax ist:

[prepasm("string" : output operands : input operands [: clober 
list]);[/pre]

Du versuchst gerade, mehrere Strings mit meheren Listen von Operanden
zu mischen.

> Abgeleitet war die Loop aus einem Beispiel, wo ein "+r" verwendet
> wurde.  Es ist nirgendwo beschrieben, scheint aber zu funktionieren.

"r" ist das constraint für einen Registeroperanden, das "+" macht ihn
read/write.  Sollte beschrieben sein, müsste ich dir raussuchen.

Ich kenne mich mit MSP430 nicht aus, aber guck dir mal sowas an:
1
    void delay_ms(int time, int count)
2
    {
3
        __asm__ __volatile__
4
        (
5
          "1:                           \n\t"
6
          " dec %[inner]                \n\t"
7
          " jne 1b                      \n\t"
8
          " dec %[outer]                \n\t"
9
          " jne 1b                      \n\t"
10
        :
11
              : [inner] "r"(time), [outer] "r"(count)
12
        );
13
    }

Vermutlich musst du dir register auch noch vorbelegen.  Im Moment ist
es bissel Humbug, weil du die erste innere Schleife mit dem Wert von
"time" (aus den Funktionsparameters) zu zählen beginnst, die folgenden
dann aber bei 0.  Vielleicht wolltest du ja sowas?
1
    void delay_ms(int time, int count)
2
    {
3
        int i;
4
        __asm__ __volatile__
5
        (
6
          "1:                           \n\t"
7
          " mov %[inner], %[preload]    \n\t"
8
          "2:                           \n\t"
9
          " dec %[inner]                \n\t"
10
          " jne 2b                      \n\t"
11
          " dec %[outer]                \n\t"
12
          " jne 1b                      \n\t"
13
              : [inner] "=&r"(i)
14
              : [preload] "r"(time), [outer] "r"(count)
15
        );
16
    }

Ich hab's rudimentär auf einem AVR-GCC getestet, die Registerzuweisung
scheint mir erstmal OK zu sein.

Eine Alternative wäre es, für [inner] ein festes Register vorzusehen
und dies in der clobber list aufzuführen.

von Gast (Gast)


Lesenswert?

Danke Jörg, damit habe ich es jetzt hinbekommen.
1
#define XTAL 4000000
2
3
#define CYCLES XTAL/1000
4
5
void delay_ms(unsigned int time)
6
{
7
  unsigned int count = CYCLES;
8
  asm volatile
9
  (
10
    "1: dec %[outer]         \n\t"
11
    "2: dec %[inner]         \n\t"
12
    " jne 2b                 \n\t"
13
    " jne 1b                 \n\t"
14
        : : [inner] "r"(count), [outer] "r"(time)
15
  );
16
}

erzeugt mir
1
mov r15, r14
2
mov #4000, r15
3
dec r14
4
dec r15
5
jnz $-2
6
jnz $-6
7
ret

Wobei in r15 time steht. Optimiert werden könnte es, wenn die übergebene 
Variable time nicht nach r15 sondern r14 geschrieben wird. Dann fällt 
natürlich die erste Zeile weg.
Anfangs wurde immer ein clr r15 eingeschoben, nicht gerade hilfreich in 
diesem Fall.

Was aus der Doku schwer raus kam:
Der erste Doppelpunkt beschreibt den Output, der zweite den Input, der 
dritte die clopper-Liste. Outputs habe ich ja keine. Schreibe ich die 
Doppelpunkte untereinander, wird ein Syntax Error ausgegeben.
In der Doku (ich habe mich an die des avr gelehnt), steht nur "r", "=r" 
und "=&r" drin. Es wäre wirklich hilfreich zu erfahren, wo genau das 
steht.

Mit einer guten Anleitung müssen die Jungs echt noch üben :-)

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


Lesenswert?

Gast wrote:

> Was aus der Doku schwer raus kam: Der erste Doppelpunkt beschreibt
> den Output, der zweite den Input, der dritte die clopper-Liste.

"clobber".

info gcc -> C Extensions -> Extended Asm

`` Each operand is described by an operand-constraint string followed
by the C expression in parentheses.  A colon separates the assembler
template from the first output operand and another separates the last
output operand from the first input, if any.''

(OK, die clobbers sind dort nicht beschrieben.)

> Outputs habe ich ja keine. Schreibe ich die Doppelpunkte
> untereinander, wird ein Syntax Error ausgegeben.

Dann hast du noch was anderes vermurkst, das geht auf jeden Fall.  Das
oben zitierte Beispiel war bei mir ja durch den Compiler gegangen (nur
assemblieren konnte ich es nicht mangels MSP430-Tools).

> In der Doku (ich habe mich an die des avr gelehnt), steht nur "r",
> "=r" und "=&r" drin. Es wäre wirklich hilfreich zu erfahren, wo
> genau das steht.

info gcc -> C Extensions -> Constraints -> Modifiers

```+' Means that this operand is both read and written by the
     instruction.''

> Mit einer guten Anleitung müssen die Jungs echt noch üben :-)

Nein, ich würde stattdessen von der Bibliothek erwarten, dass sie dir
eine entsprechende delay-Funktion zur Verfügung stellt.  Die
Entwickler der Bibliothek sollten wissen, wie man mit dem
inline-Assembler umgeht, du als Endnutzer musst das nicht unbedingt.

von Gast (Gast)


Lesenswert?

> Nein, ich würde stattdessen von der Bibliothek erwarten, dass sie dir
> eine entsprechende delay-Funktion zur Verfügung stellt.

Codevision macht das für den AVR. Die haben es tatsächlich geschafft, 
relevante Funktionen wie getchar, putchar oder delay in Assembler zu 
definieren. Vielleicht bastle ich mir da selber etwas zusammen.

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


Lesenswert?

Die avr-libc macht das auch für den AVR, aber hier ging's ja um den
MSP430.

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.