AVR ASM Frage zu Symbolen bzw Rechnen mit Symbolen

Gast #7012260
Lesenswert?

1
.equ test = 10
2

3

4
...
5

6

7

8
  ldi  r16, test-1
9
loop:
10
  cpi  r16, 9
11
  breq loop

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
Gast #7012291
Lesenswert?

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
Gast #7012455
Lesenswert?

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.
Gast #7013735
Lesenswert?

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!
Moderator (Firma: Titel) Persönliche Seite #7013809
Lesenswert?

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.
Gast #7013903
Lesenswert?

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.
1
#define TEST 10
2
...
3
x = TEST-1;
4

5
// gegenüber
6

7
#define TEST 10
8
...
9
x = TEST;
10
x--;
#7014041
Lesenswert?

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.
Beitrag #7014209 wurde von einem Moderator gelöscht.
#7014222
Lesenswert?

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.
#7014225
Lesenswert?

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.
Gast #7014232
Lesenswert?

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.
#7014255
Lesenswert?

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?
#7014265
Lesenswert?

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.
#7014300
Lesenswert?

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.
Gast #7014610
Lesenswert?

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!
#7014613
Lesenswert?

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
(Firma: Schweigstill IT) Persönliche Seite #7014614
Lesenswert?

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".
#7014615
Lesenswert?

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.
#7014618
Lesenswert?

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.
Gast #7014619
Lesenswert?

(prx) A. K. schrieb:
> Bei 68000
> Bei x86
> Bei diversen Architekturen

Andreas 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.
#7014635
Lesenswert?

(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.
#7014715
Lesenswert?

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.
#7014735
Lesenswert?

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
Gast #7014891
Lesenswert?

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.
#7016143
Lesenswert?

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.
Gast #7016486
Lesenswert?

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...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren