Hallo, es geht um den Codeschnipsel. Ich habe ein Symbol mit dem Wert 10
definiert und so lande/bleibe ich in meiner loop.
Ich spare mir durch das test-1 ein dec r16. Frage: Wo ist dokumentiert,
dass man so etwas machen darf/kann. Ich suche mir einen Wolf.
Danke
Ich kenne die AVR Toolchain zu wenig, aber füher (tm) war dafür der
"Makroprozessor (macro processor) m4" zuständig. Vielleicht hilft dir
das als Stichwort.
Zumindest unter unixoiden OS bekommst Du mit "man m4" Details angezeigt
Assembler/Compiler können konstante Ausdrücke berechnen und das Ergebnis
in den Code einsetzen. Typisch sind die Grundrechenarten, Schieben und
Bitoperationen (AND/OR/XOR/NOT) erlaubt.
Anfänger schrieb:> Ich spare mir durch das test-1 ein dec r16. Frage: Wo ist dokumentiert,> dass man so etwas machen darf/kann. Ich suche mir einen Wolf.
Das ist der Normalfall! Dafür sind Symbole da! Wenn gleich dein Beispiel
praktisch sinnlos ist, denn das ist eine Endlosschleife ;-)
Anfänger schrieb:> Ich spare mir durch das test-1 ein dec r16
Nein.
Und das gleich aus mehreren Gründen. Zum einen lädst du die Sache
außerhalb der Schleife. Das würde also nichtmal mit einer Variablen
funktionieren. Und zum anderen sind Symbole aus Laufzeitsicht sowieso
konstant.
Da wird nichts gespart. Was sollte daran dokumentatíonswürdig sein,
dass eine Konstante mit dem Wert 10 um 1 erniedrigt den Wert 9 hat?
loop:
rjmp loop
macht viel einfacher die Endlosschleife.
Noch bescheuerter (funktioniert aber!) wäre:
clr R16
loop:
breq loop
Gute Nacht Deutschland!
Anfänger schrieb:> ldi r16, test-1
Vieles wurde schon genannt.
Geht gar nicht. Wie kommst du darauf?
Dein Assembler bringt dir einen Fehler.
test-1 ist ein unmittelbarer Wert. Keine Berechnung.
Volpe "AVR-Mikrokontroller-Praxis".
michael_ schrieb:> test-1 ist ein unmittelbarer Wert. Keine Berechnung.
test-1 IST eine Berechnung, allerdings zur Assemblierzeit, nicht
Laufzeit. Somit ist er zur Laufzeit konstant. Ein Assembler kann gar
keine Laufzeitberechnungen umsetzen, das machen nur Compiler.
Anfänger schrieb:> Ich spare mir durch das test-1 ein dec r16.
Für dich sind also die folgenden zwei Beispiele "gefühlt" etwa
gleichwertig, weil sie ja das selbe Ergebnis bringen:
1
.equ test = 10
2
...
3
ldi r16, test-1
gegenüber
1
.equ test = 10
2
...
3
ldi r16, test
4
dec r16
Beide Male steht jetzt wie benötigt im r16 der Wert 9.
Der Witz ist im Grunde, dass der Wert 9, der zum Schluss im r16 steht,
im ersten Fall 1 einziges Mal vom Assembler auf 1 PC berechnet wird. Und
natürlich auch für diese Berechnung nur dieses 1 einzige Mal Zeit und
Strom und damit Energie benötigt wird.
Im zweiten Fall wird aber möglicherweise dieser Wert 9 möglicherweise
von 10 Millionen Geräten 1000 mal pro Sekunde ausgerechnet. Und die
brauchen für diese Rechnung natürlich auch jedes Mal Energie (sie
brauchen Leistung für ein bestimmte Zeit).
Das ist der eine Knackpunkt, warum es sinnvoll ist, so viele
Berechnungen wie möglich zur Compile/Assemblierung-Zeit durchzuführen.
Der zweite Knackpunkt ist, dass natürlich bei zu vielen solchen
unnötigen Berechnungen auf dem Zielsystem unnötigerweise
Programmspeicher und Rechenzeit "verbraucht" wird.
Lothar M. schrieb:> Der Witz ist im Grunde,
...
Auch wenn es hier um Assembler geht: Mir stellt sich die Frage, ob ein
ähnliches Konstrukt in z.B. C sich vergleichbar verhält oder ob dort der
Compiler beim Optimieren 'intelligenter' vorgeht und beide Varianten den
selben Code ergeben?
Also z.B.
HildeK schrieb:> Mir stellt sich die Frage, ob ein ähnliches Konstrukt in> z.B. C sich vergleichbar verhält oder ob dort der Compiler> beim Optimieren 'intelligenter' vorgeht und beide Varianten> den selben Code ergeben?
Da sollte der gleiche Code bei rauskommen ("as-if"-Regel), solange das x
nicht irgendwie besonders ist (volatile oder so).
Falk B. schrieb:> Welches Problem so hier gelöst werden?> Das ist doch alles vollkommen sinnfrei!
Lernen ist immer sinnfrei, wenn man es schon kann.
S. R. schrieb:>> Welches Problem so hier gelöst werden?>> Das ist doch alles vollkommen sinnfrei!>> Lernen ist immer sinnfrei, wenn man es schon kann.
Gelaber! Der OP redet wirr. Es löst ein NICHT-Problem!
Vielleicht ein ZEN-Lehrling?
Lothar M. schrieb:> Ich habs mal kurz probiert
Danke, hätte ich natürlich selbst auch machen können 😉; hatte gehofft,
es weiß jemand aus der Erfahrung heraus - so wie offensichtlich S.R..
Falk B. schrieb:> Ein Assembler kann gar> keine Laufzeitberechnungen umsetzen, das machen nur Compiler.
Wie bitte?
Keine Ahnung, wie das gemeint war, aber so, wie es da steht, ist es
natürlich kompletter Unsinn.
c-hater schrieb:>> Ein Assembler kann gar>> keine Laufzeitberechnungen umsetzen, das machen nur Compiler.>> Wie bitte?>> Keine Ahnung, wie das gemeint war, aber so, wie es da steht, ist es> natürlich kompletter Unsinn.
Ich meinte, daß ein Assembler (die meisten?), keine Formeln in
irgendwelchen Ausdrücken in eine dynamische Berechnung während der
Laufzeit umformen können bzw. wollen. In C ist das möglich und
vorteilhaft, manchmal auch unerwünscht.
Beispiel. Wenn man util/delay.h beim AVR nutz und keine Optimierung
eingeschaltet hat, wird ein Ausdruck
_delay_ms(100);
in eine Fließkommarechnung während der Laufzeit umgesetzt.
Eine ähnliche Konstruktion ist in Assembler, zumindest beim AVR
Assembler von Atmel, vermutlich auch beim gcc asm nicht möglich. Ich hab
jetzt auch kein gescheites Beispiel parat.
Ich rede hier nicht von Macros und ähnlichem, sondern von reinen,
numerischen Ausdrücken, sprich Formeln.
c-hater schrieb:> Keine Ahnung, wie das gemeint war, aber so, wie es da steht, ist es> natürlich kompletter Unsinn.
Assembler pflegen i.d.R. keine Berechnungen zusammenzufassen, die
ausdrücklich in Form mehrerer Befehle der Zielmaschine geschrieben
wurden - die eben eine formale Laufzeitberechnung (zur Laufzeit der
Maschine) darstellen. Das ist das Privileg des Optimizers eines
Compilers.
Es gibt aber schon Fälle, in denen Assembler Laufzeitoperationen
zusammenführen. Beispielsweise kann das geschehen, wenn ein Sprungbefehl
auf einen Sprungbefehl geht und der Assembler dann gleich ins zweite
Ziel springt.
Falk B. schrieb:> _delay_ms(100);>> in eine Fließkommarechnung während der Laufzeit umgesetzt.>> Eine ähnliche Konstruktion ist in Assembler, zumindest beim AVR> Assembler von Atmel, vermutlich auch beim gcc asm nicht möglich.
Doch, das wäre möglich.
> Ich rede hier nicht von Macros und ähnlichem, sondern von reinen,> numerischen Ausdrücken, sprich Formeln.
Solange die Formeln nur die Rechenarten enthalten, die der Assembler von
Hause aus kann, geht das. Das sind neben den Grundrechenarten auch noch
logische und binäre Grundoperationen, Exp2 und Log2 (hoffe, habe nicht
noch was vergessen). Damit geht schon eine ganze Menge.
c-hater schrieb:>> Eine ähnliche Konstruktion ist in Assembler, zumindest beim AVR>> Assembler von Atmel, vermutlich auch beim gcc asm nicht möglich.>> Doch, das wäre möglich.
Theoretisch. Wird das praktisch gemacht? Beispiel?
>> Ich rede hier nicht von Macros und ähnlichem, sondern von reinen,>> numerischen Ausdrücken, sprich Formeln.>> Solange die Formeln nur die Rechenarten enthalten, die der Assembler von> Hause aus kann, geht das. Das sind neben den Grundrechenarten auch noch> logische und binäre Grundoperationen, Exp2 und Log2 (hoffe, habe nicht> noch was vergessen). Damit geht schon eine ganze Menge.
Auch wieder, theoretisch. Mach das ein Assembler, vor allem die hier
diskutieren AVR Assembler? Wenn ja, in welchem Fall?
c-hater schrieb:>> Eine ähnliche Konstruktion ist in Assembler, zumindest beim AVR>> Assembler von Atmel, vermutlich auch beim gcc asm nicht möglich.>> Doch, das wäre möglich.
Ein Assembler, der dies
1
add r1, #1
2
add r1, #1
zu
1
add r1, #2
zusammenfasst, wäre etwas mutig, denn ein etwas schräg aufgelegter
Programmierer könnte in voller Absicht vorgesehen haben, r1 im Interrupt
auf ungerade Werte zu testen. Damit würde sich die erste Sequenz in
seltenen Fällen anders verhalten als die zweite.
(prx) A. K. schrieb:> Ein Assembler, der diesadd r1, #1> add r1, #1> zuadd r1, #2> zusammenfasst, wäre etwas mutig,
Naja, es gibt schon optimierende Assembler. Was die WIE aber optimieren,
ist mir nicht bekannt. Optimiert der AVR Assembler? Der avr gas?
Falk B. schrieb:> optimierende Assembler
Die optimieren aber nicht im Sinne von gcc und Konsorten (Ausdrücke
zusammenfassen, lange Berechnungen durch konstante Ausdrücke ersetzen,
wenn sie nicht wirklich auf Variablen arbeiten, Neuladen eines Werts
unterlassen wenn er sich seit der letzten Verwendung nicht geändert
haben sollte, toten Code erkennen und elimieren...) sondern eher auf dem
Niveau bspw. langen Sprung zu einem kurzen abändern, wenn er nah genug
ist und ähnliches.
Das ist eine ganz andere Hausnummer.
Klaus W. schrieb:> sondern eher auf dem Niveau bspw. langen Sprung zu einem kurzen> abändern, wenn er nah genug ist und ähnliches.
Hoffentlich auch das nicht.
Stell Dir nur vor, der Sprung wär Teil einer genau taktabgestimmten
Verzögerungsschleife.
Assembler ist kein Spielball für Optimierungsfantasien und das ist im
Sinne des Erfinders!
Jörn schrieb:> Hoffentlich auch das nicht.
Das ist öfter anzutreffen. Bei 68000 kannst du dann aber per Suffix die
Variante explizit festlegen.
Bei x86 lassen sich Konstanten im Befehl oft in verschiedener Länge
codieren. Der Assembler wählt die genutzte Variante abhängig vom Wert
aus, was zu unterschiedlicher Länge führt.
Bei diversen Architekturen gibt es bei manchen Befehlen mehrere
Varianten, sie zu codieren. Verschiedene Assembler können dann
verschiedene Varianten wählen. Das ist zwar dann gleichwertig, aber eben
nicht eindeutig
Jörn schrieb:> Assembler ist kein Spielball für Optimierungsfantasien und das ist im> Sinne des Erfinders!
Für einige Prozessorarchitekturen gibt es bei gebräuchlichen Assemblern
durchaus Pseudobefehle, die nach Gutdünken des Assemblers in einen oder
mehrere Maschinenbefehle übersetzt werden. Bei ARM wären das z.B. die
Befehle "ADR", "MOV32" sowie "LDR Rx, =expr". Im Gegensatz zum Z80 o.ä.
haben viele Prozessoren auch keinen dedizierten NOP-Befehl, sondern
bilden diesen auf einen wirkungslosen anderen Befehl ab, z.B. bei ARM
meist auf "MOV R0,R0" oder "MOV R8,R8".
Jörn schrieb:> Stell Dir nur vor, der Sprung wär Teil einer genau taktabgestimmten> Verzögerungsschleife.
Oft hängt die Laufzeit von Code vom Alignment im Speicher ab. Dieses
aber kann sich unbemerkt verändern, wenn man nicht sehr sorgfältig
darauf achtet. Umgekehrt kann es Pseudobefehle zur Erzwingung eines
Alignments geben, deren Umsetzung über Quasi-NOPs je nach Adresslage
weit variiert, von der gewählten Prozessorvariante abhängig ist, und von
der Version des genutzten Assemblers.
Jörn schrieb:> Assembler ist kein Spielball für Optimierungsfantasien und das ist im> Sinne des Erfinders!
Mach das bei 8051 oder AVR. Aber schon bei vielen ARMen kann das exakte
Zeitverhalten zum schwierigen bis hoffnungslosen Fall werden.
Zeitschleifen empfehle ich daher jenseits davon selbstkalibrierend zu
implementieren. Die Laufzeit einer Schleife bei Programmstart anhand
eines bekannten Timers nachmessen und dies in die künftigen Aufrufe
einrechnen.
(prx) A. K. schrieb:> Bei 68000> Bei x86> Bei diversen ArchitekturenAndreas S. schrieb:> Für einige Prozessorarchitekturen
Bei beim Microcontroller AVR und seinem Assembler gilt das alles
jedenfalls nicht. Und das ist auch gut so. Was auf dem Papier steht
sollte sich so auch 1:1 im Flash wiederfinden. Das taktgenaue Justieren
ist ja gerade einer der Vorzüge, wenn man schon in Asm schreibt.
(prx) A. K. schrieb:> Das ist öfter anzutreffen. Bei 68000 kannst du dann aber per Suffix die> Variante explizit festlegen.
Das habe ich auch noch im Hinterkopf. Auch kurze und lange Sprünge.
Solange man das per Einstellung abschalten kann, ist das OK.
Gerade bei Assembler will man WYSIWYG. Meistens.
Aber um die eigentliche Frage abschließend zu beantworten. Der AVR
Assembler, sei es der von Atmel (Assember 1 und 2) sowie der gas vom gcc
berechnen nur numerische Ausdrücke, welche zur Assemblierungszeit
konstant sind und schreiben sie als Konstante in die Befehlsparameter.
Mehr nicht. Mehr braucht es im Normalfall auch nicht. Alles andere
müssen echte Assemblerfunktionen leisten.
Falk B. schrieb:> Das habe ich auch noch im Hinterkopf. Auch kurze und lange Sprünge.> Solange man das per Einstellung abschalten kann, ist das OK.> Gerade bei Assembler will man WYSIWYG. Meistens.
Ist doch ganz entspannt:
Bei 68000 gibt es den BRA(nch)-Befehl (ebenso wie seine Varianten mit
Bedingung: BEQ, BLE...) entweder mit 16 Bit oder mit 32 Bit.
In dem eigentlichen Befehlswort mit 16 Bit ist die Hälfte für die
Sprungdistanz vorgesehen, man kann also -128 bis +127 springen.
Wenn das nicht reicht, werden diese 8 Bit komplett zu 0 gesetzt und in
folgenden Wort ist die 16-Bit-Distanz (-32768 ... +32767).
(Wenn das auch nicht reicht, muß man JMP bemühen mit 32 Bit-Adresse)
Will man in Assembler die kurze Variante, schreibt man BRA.S ... .
Will man unbedingt die große Version, schreibt man BRA.L ... .
Schreibt man nur BRA ..., dann nimmt der Assembler was er will.
Ist es ein "optimierender Assembler", dann schaut er wie groß die
Distanz ist und entscheidet dann sinnvoll.
So braucht man sich im Normalfall nicht kümmern und muß nicht wissen ob
das eine Byte reicht (dazu müsste man ja im Kopf assemblieren).
Wer es genau wissen will, schreibt halt .S oder .L hin.
Andreas S. schrieb:> Für einige Prozessorarchitekturen gibt es bei gebräuchlichen Assemblern> durchaus Pseudobefehle, die nach Gutdünken des Assemblers in einen oder> mehrere Maschinenbefehle übersetzt werden. Bei ARM wären das z.B. die> Befehle "ADR", "MOV32" sowie "LDR Rx, =expr". Im Gegensatz zum Z80 o.ä.> haben viele Prozessoren auch keinen dedizierten NOP-Befehl, sondern> bilden diesen auf einen wirkungslosen anderen Befehl ab, z.B. bei ARM> meist auf "MOV R0,R0" oder "MOV R8,R8".
Das ist ja beim AVR auch nicht anders, aber mit „optimieren“ hat das nun
nichts zu tun.
Oliver
Ohne jetzt zu viel ins Detail zu gehen: Der AVR Assembler kann genau die
"Berechnungen", die vom unterliegenden Objektformat unterstützt werden.
Beim GCC ist das ELF. Was sich in ELF nicht abbilden lässt, kann später
nicht gelinkt werden.
Hier beispielsweise die Relocations für i386:
https://docs.oracle.com/cd/E19683-01/817-3677/chapter6-26/index.html
Leider ist der AVR GCC nicht so gut dokumentiert, die entsprechende
Beschreibung finde ich gerade nicht. Aber im Prinzip werden ähnliche
Relocations unterstützt. Gerüchteweise liegen sie in
binutils/include/elf/avr.h, darf jeder selbst nachgucken.
Die notwendigen Relocations ergeben sich aus dem Instruktionsformat und
den Anforderungen von Compilern. Man könnte auch ein eigenes
Objektformat mit vielen neuen lustigen aber unnützen Relocations
definieren und dann einen Assembler aufsetzten der diese unterstützt,
bringt in der Praxis halt nur wenig.
Alle Relocations werden aufgelöst wenn das Programm an eine finale
Adresse geladen wird. Das passiert bei normalen Systemen kurz vor der
Ausführung, bei AVR wenn die ELFs in ein Binärimage mit absoluten
Adressen umgewandelt werden. Es wird also alles im Voraus berechnet.
Falk B. schrieb:> Gelaber! Der OP redet wirr. Es löst ein NICHT-Problem!> Vielleicht ein ZEN-Lehrling?
Ich habe auch schon ziemlich wirre Fragen (von Studenten) bekommen, die
man nicht sinnvoll beantworten kann. Man kann aber den Hintergrund
erklären - wie hier geschehen - und hoffen, dass die Frage verschwindet.
Jörn schrieb:>> sondern eher auf dem Niveau bspw. langen Sprung zu einem kurzen>> abändern, wenn er nah genug ist und ähnliches.> Hoffentlich auch das nicht.
Moderne Menschen haben mal untersucht, warum alte (1980er)
Microsoft-Software ständig die Kombination "short JMP + NOP" nutzt, was
ja den damals kostbaren Speicher verschwendet:
Alte Versionen vom MASM haben den Code direkt generiert und die
Zieladresse später eingetragen. Wenn der Sprung kurz genug war - was der
vorher nicht wusste - dann hat er einen schnelleren kurzen Sprung plus
NOP eingebaut. (Spätere MASM-Versionen konnten das besser.)
Ich glaube auch, dass der MIPS-Assembler den Branch Delay Slot
optimieren konnte.
> Stell Dir nur vor, der Sprung wär Teil einer genau taktabgestimmten> Verzögerungsschleife.
Dann hat der Entwickler versagt. Wenn es ein Sprung mit Codierung XY
sein muss (z.B. in einer Vektortabelle), dann muss er auch genau den
hinschreiben.
> Assembler ist kein Spielball für Optimierungsfantasien und das ist im> Sinne des Erfinders!
Ähm, also 99.9% aller Software möchte solche Optimierungen haben.
Compiler können solche Mikrooptimierungen nicht machen, also landen die
im Assembler, wo sie auch dem Programmierer nützen. Oder im Linker.
Diejenigen, die mit selbstmodifierendem Code arbeiten oder tatsächlich
auf das exakte Encoding angewiesen sind, arbeiten sowieso direkt mit den
Opcodes.
Und: Zählen Pseudo-Instruktionen wie "ADD Rn, Rn" als Ersatz für "left
shift" nicht auch zu Assembler-Magie? Die will man eigentlich auch
haben.
S. R. schrieb:> alte (1980er) Microsoft-Software> Alte Versionen vom MASM> Compiler
Halten wir nochmal fest:
Wir sind hier beim AVR.
Den hat man unter Asm im Ablauf 100% unter Kontrolle, da steht im Flash
exakt das was auf dem Papier = im Programm steht. Da läuft auch keine
andere Task nebenbei.
>> Stell Dir nur vor, der Sprung wär Teil einer genau taktabgestimmten>> Verzögerungsschleife.>> Dann hat der Entwickler versagt.
Dann versagt auch kein Entwickler sondern der hat in AVR Assembler die
Möglichkeit, einzelne Befehle wunderbar dazu einsetzen zeitlich exakt
definierte Programmschleifen und Abläufe zu kreieren. Was ist daran so
schwer zu verstehen?
> also 99.9% aller Software möchte solche Optimierungen haben.> Compiler können solche Mikrooptimierungen nicht machen, also landen die> im Assembler
Wir reden hier von 100% Assemblerprogrammen und keinem Compiler/Asm
Mischmasch. Daß Hochsprachencompilersoft nach Lust und Laune optimiert
wird steht völlig außer Frage.
Johannes schrieb:> Ohne jetzt zu viel ins Detail zu gehen: Der AVR Assembler kann genau die> "Berechnungen", die vom unterliegenden Objektformat unterstützt werden.
Geh doch mal ins Detail.
Ich möchte auch noch was lernen.
Irgendwelche berechneten Optimierungen gibts nämlich da nicht...
S. R. schrieb:> Im allgemeinen Fall nichtmal ein bisschen.
Ganz so speziell wird "genau mein Fall" schon nicht sein...
c-hater schrieb:> Falk B. schrieb:>>> Optimiert der AVR Assembler?>> Nein. Thanks God.
In den Chor möchte ich einstimmen.
Jörn schrieb:> Ganz so speziell wird "genau mein Fall" schon nicht sein...
Nun, wir haben einige Gegenbeispiele gebracht, die du mit "aber nicht
mit dem aktuellen AVR-Assembler ohne Nutzung eines Compilers"
weggewischt hast.
Manche Teller sind halt doch tiefer.