Benjamin S. schrieb:> Ich denke der Compiler optimiert das am besten:uint8_t *ptr => 0x17;> wenn du die Funktion inline verwenden willst, dann definiere sie inline.> inline void setDdrLed( uint8_t x )> {> asm (> "ldi r16, %0\n\t"> "out 0x17 , r16"> : "+r" ( x )> );> }
Okay, habe ich übersehen.
Geht aber nach wie vor nicht.
Benjamin S. schrieb:> Du kannst x mit 17 belegen beim AufrufsetDdrLed(0x17)> oder willst du beim "out 0x17 , r16" das 17 dynmisch haben?> inline void setDdrLed( uint8_t x, uint8_t reg )> {> asm (> "ldi r16, %0\n\t"> "out %1 , r16"> : "+r" ( x )> : "+r" ( reg )> );> }>> weitere Info gibts u.a. hier:> https://rn-wissen.de/wiki/index.php?title=Inline-A...
Ich würde gerne in das Register bzw. an die Adresse 0x17 den Wert den
ich übergebe, rein schreiben.
Thorsten K. schrieb:> Ich würde gerne in das Register bzw. an die Adresse 0x17 den Wert den> ich übergebe, rein schreiben.
Das kannst du auch ohne Assembler machen:
1
staticfunc(uint8_tx){
2
*(uint8_t*)0x17=x;
3
}
Oder besser lesbar, wie bereits geschrieben wurde:
1
staticfunc(uint8_tx){
2
uint8_t*ptr=(uint8_t*)0x17;
3
*ptr=x;
4
}
Wenn es dir darum geht, den Inline-Assembler von gcc besser zu
verstehen, dann sage das bitte deutlich an.
S. R. schrieb:> Thorsten K. schrieb
schrieben wurde:static func(uint8_t x)
> {> uint8_t *ptr = (uint8_t*)0x17;> *ptr = x;> }>> Wenn es dir darum geht, den Inline-Assembler von gcc besser zu> verstehen, dann sage das bitte deutlich an.
Ja ich würde es gerne in Inline Assembler machen, damit ich es auch
besser verstehe..
>> oder willst du beim "out 0x17 , r16" das 17 dynmisch haben?>>
1
>inlinevoidsetDdrLed(uint8_tx,uint8_treg)
2
>{
3
>asm(
4
>"ldi r16, %0\n\t"
5
>"out %1 , r16"
6
>:"+r"(x)
7
>:"+r"(reg)
8
>);
9
>}
10
>
Welcher μC soll das denn sein? Aus dem ersten Post zu schließen ein
AVR8.
Wo hat der ein OUT mit Port-Adresse in einem Register? Das geht nur über
die MEM-Adresse (idR IO-Adresse+0x20 also 0x37) in einem
Pointer-Register.
> Ich würde es halt mal gerne wissen wie man das damit macht.
Wie wär's mit Google? Ne Suche nach "avr-gcc inline assembler" gibt als
ersten deutschen[1] Treffer:
https://rn-wissen.de/wiki/index.php?title=Inline-Assembler_in_avr-gcc
[1] Wobei ich mir nicht sicher bin, ob Deutsch die passende Sprache ist
;-)
foobar schrieb:>> Ich würde es halt mal gerne wissen wie man das damit macht.>> Wie wär's mit Google? Ne Suche nach "avr-gcc inline assembler" gibt als> ersten deutschen[1] Treffer:>> https://rn-wissen.de/wiki/index.php?title=Inline-A...>> [1] Wobei ich mir nicht sicher bin, ob Deutsch die passende Sprache ist> ;-)
Ja habe ich schon gegoogelt.. Diese Seite habe ich auch schon
aufgerufen.. Bin nicht schlauer geworden..
> Diese Seite habe ich auch schon aufgerufen.. Bin nicht schlauer> geworden..
Die Seite erklärt aber gut und ausführlich. Besser wirst du's hier auch
nicht bekommen. Evtl solltest du dir nen anderes Thema suchen ...
foobar schrieb:>> Diese Seite habe ich auch schon aufgerufen.. Bin nicht schlauer>> geworden..>> Die Seite erklärt aber gut und ausführlich. Besser wirst du's hier auch> nicht bekommen. Evtl solltest du dir nen anderes Thema suchen ...
Man könnte es jedoch wenigstens mal versuchen.
Ich kann auf den Port zugreifen. Das heißt wenn ich jetzt ne LED dran
hänge fängt diese auch zu leuchten, je nach dem welches Bit ich setze.
Bei den Ausgängen jedoch funktioniert das eben nicht. Muss doch einen
einfachen Grund haben..
Thorsten K. schrieb:> Geht aber nach wie vor nicht.
Was heißt "geht nicht"? Nimmt er Compiler es nicht an? Kommt der Wert
nicht im Register an? Sollte es so sein: Woran hast du das erkannt?
Rolf M. schrieb:> Thorsten K. schrieb:>> Geht aber nach wie vor nicht.>> Was heißt "geht nicht"? Nimmt er Compiler es nicht an? Kommt der Wert> nicht im Register an? Sollte es so sein: Woran hast du das erkannt?
Der Compiler zeigt mir keine warnings oder Fehlermeldungen an.
Ich vermute mal das der Wert nicht ins Register rein geschrieben wird.
Thorsten K. schrieb:> foobar schrieb:>> Die Seite erklärt aber gut und ausführlich. Besser wirst du's hier auch>> nicht bekommen. Evtl solltest du dir nen anderes Thema suchen ...>> Man könnte es jedoch wenigstens mal versuchen.> Ich kann auf den Port zugreifen. Das heißt wenn ich jetzt ne LED dran> hänge fängt diese auch zu leuchten, je nach dem welches Bit ich setze.>> Bei den Ausgängen jedoch funktioniert das eben nicht.
Du sprichst in Rätseln. Was meinst du denn bitte mit "den Ausgängen"?
Inwiefern unterscheiden sich irgendwelche Ausgänge von Portpins, an
denen LED hängen? Und was genau soll "funktioniert nicht" heißen?
Klingt gerade überhaupt nicht nach einem Softwareproblem, am wenigsten
nach einem Problem mit Inline-Assembler.
Peter D. schrieb:> Nun, x ist ja bereits im Register r24 (das macht der Aufrufer).
In welchem Register das an die Funktion übergeben wird, muss deinem
asm-Block egal sein. Wesentlich ist nur, dass das "+r" sagt, dass dein
Code den Wert in einem (beliebigen) Register erwartet. ldi will aber
einen Immediate-Wert und kein Register als zweiten Operanden.
Peter D. schrieb:> void setDdrLed( uint8_t x )> {> asm volatile (> "mov r16, %0\n\t"> "out %1 , r16\n\t"> : "+r" ( x )> : "M" (_SFR_IO_ADDR (DDRB))> );> }
Warum ist x hier als Output-Operand aufgeführt? Es wird doch
ausschließlich gelesen.
Man könnte sich den Umweg über r16 übrigens noch sparen. Und man kann
den Operanden zur Übersicht auch Namen geben. Dann würde das ganze so
aussehen:
Carl D. schrieb:> Mario M. schrieb:>> Wozu Inline-Assembler, wenn man auch "DDRB = x" schreiben kann?>> Weil "DDRB" hier "uint8_t reg" heißt?!
Genau darum kann es auch gar nicht gehen, C hin, Inline-Assembler her.
Der Operand für OUT muss eine Compilezeit-Konstante sein, denn die
Adresse des Ports wird in den Befehl hinein codiert. Deshalb ist ja
auch OUT auf einen vergleichsweise kleinen Portadressraum eingeschränkt.
Wenn es unbedingt zur Laufzeit einstellbar sein soll, kann man nur üben
den (langsameren) Alias der Portadresse im Speicheradressraum gehen. Das
ist in der avr-libc-FAQ beschrieben.
Rolf M. schrieb:> asm volatile (> "out %[Register], %[Value]\n\t"> : /* Leere Liste */> : [Value]"r" ( x ), [Register]"M" (_SFR_IO_ADDR (DDRB))> );
Das sieht viel versprechend aus ;) werde ich später mal testen.
Thorsten K. schrieb:> Das sieht viel versprechend aus
Ist allerdings nicht das, was du oben gewollt hast: PORTB muss hier als
Makro übergeben werden. Den kann man nicht durch einen Variablennamen
ersetzen.
Ansonsten ist das aber wirklich nichts anderes, als gleich in C zu
schreiben:
Jörg W. schrieb:> Ist allerdings nicht das, was du oben gewollt hast: PORTB muss hier als> Makro übergeben werden. Den kann man nicht durch einen Variablennamen> ersetzen.
Wo hat er das denn gewollt?
Thorsten K. schrieb:> Ich würde gerne in das Register bzw. an die Adresse 0x17 den Wert den> ich übergebe, rein schreiben.
Ich interpretiere das so, dass der Wert als dynamischer Parameter
übergeben werden soll, aber nicht die Adresse des Registers.
> Ansonsten ist das aber wirklich nichts anderes, als gleich in C zu> schreiben:> PORTB = x;
Es geht ihm ja nur darum, inline assembler zu verstehen. Da ist es nicht
von Nachteil, mit was einfachem anzufangen.
Jörg W. schrieb:> Ist allerdings nicht das, was du oben gewollt hast: PORTB muss hier als> Makro übergeben werden. Den kann man nicht durch einen Variablennamen> ersetzen.>> Ansonsten ist das aber wirklich nichts anderes, als gleich in C zu> schreiben:> PORTB = x;
Das ist mir soweit auch klar -.-*
Ich möchte doch einfach nur mein Wissen in Inline ASM ein wenig
verstärken.. Und durch das belesen, bin ich halt auch nicht weiter
gekommen..
Jörg W. schrieb:> Ist allerdings nicht das, was du oben gewollt hast: PORTB muss hier als> Makro übergeben werden. Den kann man nicht durch einen Variablennamen> ersetzen.
Wie meinst du das? Die Adresse von PORTx? Welches Makro?
Thorsten K. schrieb:> Und durch das belesen, bin ich halt auch nicht weiter gekommen.
Dann schreib lieber mal, wo du nicht weiterkommst.
Das relativ simple Beispiel mit den IO-Port-Ausgaben steht im genannten
Inline Asm Cookbook ja bereits ziemlich weit oben. Nach unten wird's
dann immer tiefgehender.
Diese Constraints mögen auf den ersten Blick ziemlich verwirrend
aussehen. Letztlich bilden sie das ab, was der Compiler intern in seinen
Zwischensprachen ebenfalls benutzt, um die einzelnen Operanden zu
markieren. Dadurch kann man die Aufteilung, welche Daten beispielsweise
in Registern gehalten werden und welche im Speicher bleiben (und dann
temporär in Register kopiert werden müssen), bis zu einem Zeitpunkt nach
hinten schieben, bei dem der Optimierer dann entscheiden kann, was wie
zugegriffen wird, sodass der gesamte Code möglichst effizient wird.
„Harte“ Registerzuteilungen sollte man folglich auch im Inline-Assembler
vermeiden und stattdessen Variablen mit passendem Constraining benutzen.
Schau dir dein einleitendes Beispiel an: du hast Register r16 hart
vorgegeben (und müsstest es in die "clobber"-Liste eintragen). Wenn du
stattdessen deinen OUT-Befehl mit einem Operanden versiehst, der
passendes Constraining bekommt (OUT braucht ein beliebiges Register,
also "r"), dann kann der Compiler sich darum kümmern, dass der
auszugebende Wert gleich mal in einem solchen Register berechnet wird,
sodass man nicht nochmal umkopieren muss.
1
voidsetled(uint8_tvalue)
2
{
3
asm("out %[port], %[val]"
4
:/* no output operands 1) */
5
:[port]"I"(_SFR_IO_ADDR(PORTB)),
6
[val]"r"(value));
7
}
1) Natürlich wird aus dem Controller etwas ausgegeben, aber das
Inline-Asm-Statement gibt in diesem Kontext nichts in seine C-Umgebung
zurück. Daher gibt es hier nur input operands.
Obige Funktion compiliert zu:
1
.global setled
2
.type setled, @function
3
setled:
4
/* prologue: function */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
/* #APP */
9
; 6 "setled.c" 1
10
out 24, r24
11
; 0 "" 2
12
/* #NOAPP */
13
ret
r24 ist hierbei das Register, in dem die Funktion den ersten Parameter
übergeben bekommt. Der Compiler muss nichts extra umkopieren, er kann
dieses Register gleich für den OUT-Befehl benutzen. Bei deiner Variante
hätte er es erst (sinnlos) nach r16 umkopieren müssen.
Thorsten K. schrieb:> Und kann ich meiner Funktion auch "PORTB" mitgeben?
Nein. Schrieb ich doch weiter oben, sowas gibt die AVR-Architektur
schlicht nicht her.
Jörg W. schrieb:> Thorsten K. schrieb:> Das sieht viel versprechend aus>> Ist allerdings nicht das, was du oben gewollt hast: PORTB muss hier als> Makro übergeben werden. Den kann man nicht durch einen Variablennamen> ersetzen.>> Ansonsten ist das aber wirklich nichts anderes, als gleich in C zu> schreiben:PORTB = x;
Also Makro übergeben. Kannst du mir bitte ein konkretes Beispiel
posten..?
Wenn PORTB oder DDRB ein Makro ist, müsste ich das doch übergeben
können?
Thorsten K. schrieb:> Wenn PORTB oder DDRB ein Makro ist, müsste ich das doch übergeben> können?
Nein. Mach dich mal mit dem Konzept von Makros vertraut: das ist doch
nur eine Textersetzung. Er würde ja bei der Übergabe aufgelöst werden
müssen.
Unabhängig von Makro oder nicht, scheinst du immer noch nicht verstanden
zu haben, dass beide Operanden eines OUT-Befehls zu dem Zeitpunkt, da
es der Assembler vorgesetzt bekommt, feststehende Konstanten sein
müssen. Das beißt sich mit deinem Wunsch, sie als Parameter einer
Funktion zu übergeben, denn dann würde man ja den konkreten Port erst
zur Laufzeit festlegen. Wenn du sowas willst, musst du über die
Port-Aliase im Speicheradressbereich gehen. Wie das geht, ist hier
beschrieben:
https://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass
Jörg W. schrieb:> Unabhängig von Makro oder nicht, scheinst du immer noch nicht verstanden> zu haben, dass beide Operanden eines OUT-Befehls zu dem Zeitpunkt, da es> der Assembler vorgesetzt bekommt, feststehende Konstanten sein müssen.> Das beißt sich mit deinem Wunsch, sie als Parameter einer Funktion zu> übergeben, denn dann würde man ja den konkreten Port erst zur Laufzeit> festlegen. Wenn du sowas willst, musst du über die Port-Aliase im> Speicheradressbereich gehen. Wie das geht, ist hier beschrieben:>> https://www.nongnu.org/avr-libc/user-manual/FAQ.ht...
Das geht dann aber nur in C Code?
Thorsten K. schrieb:> Das geht dann aber nur in C Code?
Was geht nur in C-Code? Zugriff auf IO-Ports über Speicherbereiche?
Natürlich geht das auch direkt in Assemblercode, der C-Compiler erzeugt
ja letztlich nur Assemblercode. Die Speicheradresse eines IO-Ports
bekommt man mit _SFR_MEM_ADDR() – steht natürlich alles in der Doku. ;-)
Mir stellt sich trotzdem noch die Frage, warum du für simple Dinge, die
in C völlig unproblematisch und einfach gehen, erst die Verrenkungen
über inline asm machen willst. Wirklich lernen kann man an der Stelle
nicht so viel …