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:
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.
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.
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.
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.
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
strbr2,[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:
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(,“)
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...
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.
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.
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(,“)
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.
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!
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.
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.