Forum: Mikrocontroller und Digitale Elektronik Zeiger an inline ASM übergeben


von Marcel K. (viewer)


Lesenswert?

Hallo Forum Gemeinde,

ich versuche gerade einige C- Funktionen in ASM nachzubilden.
Das klappt mit normalten Funktions- Übergabe Parameter mittlerweile auch 
ganz gut.
Ich schaffe es aber einfach nicht, eine Funktion mit einem Zeiger 
nachzubilden.

Vielleicht kann mir hier im Forum jemand weiter helfen was ich für einen 
Zeiger im ASM Befehl einsetzen muss.

Die Funktion welche ich als Beispiel umschreiben möchte sieht so aus:
1
void setValueWithPoiner(uint8_t* p_value, uint8_t shift, uint32_t value)
2
{
3
  *(p_value) = (value >>= shift);
4
}
Die ASM Funktion welche ich nicht zum Laufen bekomme, sieht bei mir im 
Moment so aus:
1
void setValueWithPoiner(uint8_t* p_value, uint8_t shift, uint32_t value)
2
{
3
  int res = 0;
4
5
  asm volatile(
6
  // es wird der Wert von "value", Anzahl von "shift" nach rechts geschoben
7
  // und in der internen Variable "res" gespeicher
8
  "lsr %[result], %[input_value], %[input_shift] \n\t"
9
  // der Wert von "res" an den Zeiger übergeben 
10
  "mov %[input_p_val], %[result]  \n\t"
11
12
  : [result] "=r" (res)
13
  : [input_p_val] "r" (*p_value),  [input_value] "r" (value), [input_shift] "r" (shift)
14
  );
15
16
  return res;  
17
}
Ich habe bereits unzählige Internet Seiten durchforstet aber kein 
wirkliches Beispiel gefunden welches mir helfen kann.
Ich weiß nicht ob es vielleicht an der Zuweisung des Registers von 
[input_p_val] liegt. Sollte es anstatt „r“ vielleicht „b“ sein? Das hat 
aber auch nicht funktioniert.

Ich würde mich über eine Antwort sehr freuen,

Viele Grüße und ein schönes Wochenende.

von Rolf M. (rmagnus)


Lesenswert?

Um welche Architektur geht es denn?

von Nils P. (torus)


Lesenswert?

Bei der übergabe von p_value an den Assembler derefernzierst Du den 
Pointer bereits. Das ist falsch. Probier mal so:

  : [input_p_val] "r" (p_value),  [input_value] "r" (value), 
[input_shift] "r" (shift)

Bei den Clobber Parametern solltest Du jetzt auch "memory" übergeben, da 
dein Assembler Code den Inhalt vom Speicher ändert.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Aus (Inline-)Assembler Sicht ist ein Pointer einfach nur ein Integer. Da 
gibt es keine spezielle Syntax für. Das hier:
1
[input_p_val] "r" (*p_value)
ist aber falsch, denn du lädst (!) hier das Ziel von p_value (was die 
C-Version ja nie tut) und übergibst den Wert in das Register 
input_p_val. Das Register überschreibt der Inline-Assembler Code dann 
mit "mov".

Möchtest du im Assembler-Code direkt in den Speicher schreiben? Dann 
musst du die Adresse selbst über ein Register an den Inline-Assembler 
übergeben:

[input_p_val] "r" (p_value)

Da ich vermute dass es sich um ARM-Assembler handelt, musst du mit "STB" 
den Wert abspeichern:
1
STB %[result], [%[input_p_val]]


Marcel K. schrieb:
> return res;
Die Funktion hat Rückgabetyp "void"...

Warum hat die C-Version überhaupt ">>="? Da reicht ">>". Warum übersetzt 
du so etwas überhaupt nach Assembler? Solche Berechnungen kann der 
Compiler normalerweise besser optimieren; insbesondere kann er sie mit 
anderen Berechnungen zusammenführen (Datenflussanalyse). Bei Inline 
Assembler kann er das nicht. Portabel ist es dann natürlich auch nicht 
mehr.

von Rolf M. (rmagnus)


Lesenswert?

Niklas G. schrieb:
> Aus (Inline-)Assembler Sicht ist ein Pointer einfach nur ein Integer.

Je nach Architektur (und deshalb hab ich auch danach gefragt) kann es 
aber sein, dass der nicht in jedem beliebigen Register stehen darf.

von Marcel K. (viewer)


Lesenswert?

Hallo alles zusammen,

erst mal recht herzlichen Dank für Eure Antworten!

@rmagnus
Du hast Recht, dass habe ich völlig vergessen zu erwähnen. Es handelt 
sich hier um eine ARM Architektur. Ich verwende für meine erste ASM 
Versuche einen Microchip/Atmel SAMD20.

@torus
Danke für Deinen Hinweis. Ich habe jetzt auch Deinen erwähnten "Clobber 
Parameter" hinzugefügt.

@erlkoenig
Auch Dir vielen Dank für Deine Hinweise. Du hast Recht, das „=“ hätte 
ich auch weg lassen können. Ich weiß nicht weshalb ich es da rein 
gemacht habe. Es funktioniert auch ohne!

Der Rückgabewert ist versehentlich drin geblieben, weil ich mit dem 
Return Wert sehen wollte, ob die Funktion richtig arbeitet. So konnte 
ich mir bei debuggen den Wert von „res“ anschauen.

Weshalb ich überhaupt mit dem „Inline assembly“ arbeite? Mir ist schon 
klar, dass der Compiler das besser kann als ich. Allerdings möchte ich 
mich ein wenig in Assembler einarbeiten und daher schreibe ich absolut 
primitive Funktionen mit der „Inline assembly“ um ein Gefühl dafür zu 
bekommen, wie das Ganze funktioniert. Auf jeden Fall hat mir Dein 
Beitrag weiter geholfen.


Ich konnte dank der letzten Feiertage, mir etwas Zeit nehmen und mich 
noch mal um die ASM Routine kümmern. Ich habe viel Zeit im Internet 
verbracht und auch viel gelesen. Das Gute ist, dass ich es jetzt hin 
bekommen habe. Es war anfangs zum Haare raufen weil ich es einfach nicht 
hin bekommen habe. Ich habe in der Funktion Hilfsvariablen angelegt und 
sie auch als "input operand" definiert um mit ihnen zu arbeiten und um 
zu verstehen was die einzelnen ASM Befehle machen. Das hat aber 
überhaupt nicht funktioniert. Beim debuggen hatte ich z.B. im 
Disassembly Anweisungen wie:
1
strb r2, [r2]
Dieser Befehl hat dann dafür gesort, dass der Controller in den Reset 
gerannt ist. Ich weiß nicht wie der Compiler zu solchen Ergebnissen 
gekommen ist.
Die Lösung war zum Schluss aber ganz einfach. Ich habe für die 
Berechnungen keine Hilfsvariable mehr verwendet, sondern gleich mit den 
„input operands“ gearbeitet.
Dann hat plötzlich alles sofort funktioniert!

Die primitive Funktion sieht nun wie folgt bei mir aus:
1
void setValueWithPoiner(uint8_t* p_value, uint32_t shifting, uint32_t value)
2
{
3
  uint8_t res;
4
5
  asm volatile(
6
  // shifte den input Wert um die Anzahl von "shifting" nach rechts 
7
  "lsr %[input_value], %[input_shift] \n\t"
8
  // schreibe den "geschobenen" Wert in den "output operand"
9
  "mov %[result], %[input_value]    \n\t"
10
  // schreibe lediglich den Bytewert an die Zeiger Adresse 
11
  "strb %[result], [%[input_p_val]]  \n\t"
12
  : [result] "=r" (res)
13
  : [input_p_val] "r" (p_value),   [input_shift] "r" (shifting),  [input_value] "r" (value)
14
  : "memory"
15
  );
16
}
Danke an alle und jetzt kann ich mich mal an die nächsten primitiven 
Funktionen ran trauen :)

Allen noch einen schönen Feiertag und viele Grüße,
Marcel(,“)

von Niklas Gürtler (Gast)


Lesenswert?

Marcel K. schrieb:
> Dieser Befehl hat dann dafür gesort, dass der Controller in den Reset
> gerannt ist

Wenig überraschend...

Marcel K. schrieb:
> Ich weiß nicht wie der Compiler zu solchen Ergebnissen gekommen ist.

Indem die Register nicht korrekt angefordert wurden. Der Compiler kann 
nicht in den Inline Assembler herein schauen. Er nimmt an, dass die 
Inputs sofort am Anfang gelesen werden und die Outputs nur am Ende 
überschrieben werden. Daher kann er r2 sowohl als Input als auch als 
Output benutzen. Das geht schief wenn man es erst beschreibt und dann 
liest. Das kann man lösen indem man es als In-Out-Operand definiert.

Das "mov" ist überflüssig...

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Marcel K. schrieb:
> Dann hat plötzlich alles sofort funktioniert!

Ist aber leider immer noch nicht korrekt und funktioniert nur 
"zufällig". Der Compiler könnte hier auch für "result" und "input_p_val" 
das selbe Register wählen. Du musst "input_value" mit "+r" in die Output 
List verschieben um das zu vermeiden. Inline Assembler ist leider 
ziemlich hinterhältig...

PS: Kennst du das hier?
http://www.ethernut.de/en/documents/arm-inline-asm.html
https://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/

von Marcel K. (viewer)


Lesenswert?

Hallo und guten Morgen,

@erlkoenig
Hmmm, jetzt beim Antworten habe ich es erst kapiert. Du hast den 
vorherigen Beitrag auch beantwortet, allerdings noch als Gast :o)
=> BTW… coole webpage!
Erst mal wieder Danke für Deinen Hinweis. Ich habe mich schon gewundert 
weshalb man das Ganze über eine interne Variable und einen 
Zwischenschritt mit „mov“ machen muss. Das hat mich schon sehr gestört 
aber ich war nur froh, dass es endlich geklappt hat :)
Nach Deinen ersten Beitrag heute Morgen habe ich bereits weiter im 
Internet gesucht und bin hier:

https://wiki.cdot.senecacollege.ca/wiki/Inline_Assembly_Language

fündig geworden.
Aber trotzdem vielen Dank für das wiederholte Schreiben. Deinen ersten 
Link habe ich gekannt. Über diese Seite bin ich schon einige malen in 
den letzten Tagen vorbei „gehuscht“. Jetzt wenn ich die Seite noch mal 
in Ruhe lese, hätte ich eigentlich selbst drauf kommen können. Die 
zweite Seite kannte ich aber noch nicht. Vielen Dank!!

Dein Hinweis mit der Definition für in- und output, ist ein weitere 
Knackpunkt und macht das Ganze jetzt wieder etwas einfacher alles zu 
verstehen. (und jetzt auch irgendwie logisch als in- und output)
Ich konnte jetzt auf die unnötige, interne Variable und den „mov“ Befehl 
völlig verzichten.
1
void setValueWithPoiner(uint8_t* p_value, uint32_t shifting, uint32_t value)
2
{
3
  asm volatile(
4
  // shifte den input Wert um die Anzahl von "shifting" nach rechts
5
  "lsr %[in_out_value], %[input_shift]    \n\t"
6
  // schreibe vom "geschobenen" Wert lediglich den Bytewert an die Zeiger Adresse
7
  "strb %[in_out_value], [%[input_p_val]]  \n\t"
8
  // mit "+r" wird "value" als "input" und als "output" Register definiert
9
  : [in_out_value] "+r" (value)
10
  : [input_p_val] "r" (p_value),   [input_shift] "r" (shifting)
11
  : "memory"
12
  );
13
}
Super! Nochmals vielen Dank. Das hilft mir jetzt bei meinen weiteren ASM 
Versuche!

Viele Grüße,
Marcel(,")

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Marcel K. schrieb:
> Hmmm, jetzt beim Antworten habe ich es erst kapiert. Du hast den
> vorherigen Beitrag auch beantwortet, allerdings noch als Gast :o)

Ja hatte meine Login-Daten nicht zur Hand ;-)

Marcel K. schrieb:
> => BTW… coole webpage!
Danke ^^

Ja der Code sieht jetzt besser aus. Ich würde "__asm__" statt "asm" 
schreiben weil das eine Compiler-Erweiterung ist; kompiliert man mit 
strikter Standard-Konformität (z.B. -std=c99 statt -std=gnu99) gibt's 
sonst Fehler.

von Marcel K. (viewer)


Lesenswert?

Hi,

OK!! Wieder ein neuer, hilfreicher Hinweis!

Ich hatte in den letzten Tagen aus dem Internet teilweise Code Beispiele 
verwendet bei denen ich dann plötzlich Fehler beim Kompilieren erhalten 
habe, obwohl es nach meinem Verständnis hätte funktionieren müssen.
Vom Gefühl her dachte ich mir, dass ich vielleicht etwas bei den Linker 
Options anpassen muss. Habe da aber nichts eindeutiges gefunden. 
Vermutlich wäre das meine Lösung gewesen :o)

Merci für den „unscheinbaren“ Hinweis!

Viele Grüße,
Marcel(,“)

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

PS: Der letzte Code braucht so immer 3 Register. Du könntest 
"in_out_value" komplett weglassen und "input_shift" überschreiben; dazu 
muss das mit "+r" zum In-Out-Wert werden, weil es ja vor der 
Verwendung von "input_p_val" verändert wird. Wenn der Compiler den 
Original-Wert aber noch braucht, wird er ihn automatisch in einem 
anderen Register sichern/kopieren; wenn nicht, spart man das Register.

Ja einfach irgendwelche Code-Beispiele kopieren geht meistens schief :) 
bei so etwas hakeligem wie Inline Assembler muss man sich genau 
anschauen, was man macht.

von Marcel K. (viewer)


Lesenswert?

Hi,

OK, was Sie meinen, habe ich verstanden. Aber weiß nicht wie ich es 
anwenden soll.
Ich benötige doch ein Register in dem ich sowohl den Wert welchen ich 
schieben möchte, als auch die Anzahl der „Schiebungen“, 
zwischenspeichern kann?
HINWEIS! CODE FUNKTIONIERT SO NICHT!
1
void setValueWithPoiner(uint8_t* p_value, uint32_t shifting, uint32_t value)
2
{
3
  __asm__ volatile(
4
  // shifte den input Wert um die Anzahl von "shifting" nach rechts
5
  "lsr %[in_out_shift], %[in_out_shift]    \n\t"
6
  // schreibe vom "geschobenen" Wert lediglich den Bytewert an die Zeiger Adresse
7
  "strb %[in_out_value], [%[input_p_val]]  \n\t"
8
  // mit "+r" wird "value" als "input" und als "output" register definiert
9
  : [in_out_shift] "+r" (shifting)
10
  : [input_p_val] "r" (p_value)
11
  : "memory"
12
  );
13
}
Wie bekomme ich nun in [in_out_shift] sowohl den Wert von „value“ als 
auch von „shifting“?
Dafür benötige ich doch eine zweite Variabel?
Grüße,
Marcel

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Ach, ich hab was durcheinander geworfen. So wie du es um 09:24 gemacht 
hattest war es schon genau richtig. Mit 2 Registern geht es natürlich 
überhaupt nicht. Dein Code braucht 3 und der Compiler wird falls nötig 
das Original in einem 4. sichern, besser geht's eh nicht.

von Marcel K. (viewer)


Lesenswert?

OK!
Danke für das feedback!
Dann mach ich mal weiter mit den nächsten Versuchen :o)

Grüße,
Marcel (,")

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Viel Erfolg! Schau dir mit "arm-none-eabi-objdump -d Prog.elf" den 
diassemblierten Code an und kompiliere mit verschiedenen -O0/1/2 
Einstellungen um zu schauen ob die Register immer sinnvoll zugewiesen 
werden.

von Rolf M. (rmagnus)


Lesenswert?

Wenn man die in/out/clobber-Listen richtig gesetzt hat, ist das volatile 
übrigens nicht nötig.

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.