Folgendes Macro soll mit fetch_byte <irgendein Register> verwendet
werden.
1
.macro fetch_byte
2
.if @0==r16
3
rcall read_byte
4
.else
5
push r16
6
rcall read_byte
7
mov @0,r16
8
pop r16
9
.endif
10
.endm
Die Subroutine read_byte liefert ein Byte in r16 zurück, wurde das Macro
mit fetch_byte r16 benutzt, braucht's das push/pop und mov natürlich
nicht.
Beim Assemblieren wird allerdings der Fehler "error: syntax error,
unexpected REGISTER" geworfen, .if kommt offenbar nicht direkt mit einem
Registernamen zurecht.
Hat jemand einen Tip, wie das zum Laufen zu bekommen ist?
Versuch mal, das Register als Zahl zu übergeben, und in Befehlen als r@0
zu verwenden. Als reine Textersetzung verstanden ginge das, aber ob der
ASM das verdaut, weiss ich nicht.
Eine andere ähnlich fiese Variante wäre
read_byte_@0
und 32 Makros der Art read_byte_r0, read_byte_r1, ...
Horst M. schrieb:> Folgendes Macro soll mit fetch_byte <irgendein Register> verwendet> werden.
[...]
> Hat jemand einen Tip, wie das zum Laufen zu bekommen ist?
Also, mal abgesehen davon, dass so ein Makro schon vom Ansatz her für
einen Assemblerprogrammierer ein Art Offenbarungseid darstellt, könnte
aber die gewünschte Funktionalität mit einem tiefen Griff in die Kiste
der schmutzigen Makrotricks durchaus gelöst werden, nämlich so:
Ich würds einfach so machen, wie ein guter C-Compiler. Ein Satz von
Registern wird als Scratchpad reserviert, die jeder nach Belieben
zerstören kann. Damit vermeidet man riesige Push/Pop Arien, weil viele
Übergabeparameter nach dem Return eh nicht mehr gebraucht werden. Und
falls doch, muß sich eben der Aufrufer darum kümmern. Das spart ne Menge
Code, als auf Verdacht alles zu pushen/poppen, bis der Arzt kommt.
(prx) A. K. schrieb:> Versuch mal, das Register als Zahl zu übergeben, und in Befehlen als r@0> zu verwenden. Als reine Textersetzung verstanden ginge das, aber ob der> ASM das verdaut, weiss ich nicht.
Funktioniert.
Statt der Pointer-Registernamen (XL, ZH etc.) müßte man zwar die
Registernummern angeben, das hätte mich allerdings im konkreten Fall
nicht gestört.
(prx) A. K. schrieb:> und 32 Makros der Art read_byte_r0, read_byte_r1, ...
Naja, ein bißchen weniger aufgeblasen wäre schon OK...
c-hater schrieb:> Also, mal abgesehen davon, dass so ein Makro schon vom Ansatz her für> einen Assemblerprogrammierer ein Art Offenbarungseid darstellt
Manchmal reichen eben zunächst 80% und ein Goldrand muß auch nicht immer
drumrum sein...
Aber Dein Vorschlag hat alles, was das Macro braucht.
Daumen hoch!
Again what learned.
Peter D. schrieb:> Ich würds einfach so machen, wie ein guter C-Compiler.> Ein Satz von Registern wird als Scratchpad reserviert
Vielleicht das nächste Mal...
c-hater schrieb:> .macro fetch_byte> .set r16_macroprobe = 0> .set @0_macroprobe = 1> .if r16_macroprobe> rcall read_byte> .else> push r16> rcall read_byte> mov @0,r16> pop r16> .endif> .endm
Zuerst herzlichen Dank an c-hater. Seine Empfehlung habe ich einstudiert
und bei Macro verwendet.
Nun aber was mir dabei noch fehlt:
In defs.inc habe ich definiert:
1
#ifndef _SETR_
2
#define _SETR_
3
.macro setr16 ; fuer 1 Byte, wo nur zwischen r0-r15 und r16-r31 zu unterscheiden ist
4
.set r16_mpr = 0
5
.set r17_mpr = 0
6
.set r18_mpr = 0
7
.set r19_mpr = 0
8
.set r20_mpr = 0
9
.set r21_mpr = 0
10
.set r22_mpr = 0
11
.set r23_mpr = 0
12
.set r24_mpr = 0
13
.set r25_mpr = 0
14
.set r26_mpr = 0
15
.set r27_mpr = 0
16
.set r28_mpr = 0
17
.set r29_mpr = 0
18
.set r30_mpr = 0
19
.set r31_mpr = 0
20
.set X_mpr = 0
21
.set xl_mpr = 0
22
.set xh_mpr = 0
23
.set Y_mpr = 0
24
.set yl_mpr = 0
25
.set yh_mpr = 0
26
.set Z_mpr = 0
27
.set zl_mpr = 0
28
.set zh_mpr = 0
29
.endm
30
.macro setr ; fuer mehrere Byte, wo die Register einzeln zu behandeln sind
Um kürzere Schreibweise von Macro zu ermöglichen, habe ich mit .def
probiert. Allerdings stört hier, daß in *.inc-Dateien von Assembler XYZ
schon mit .def angegeben sind:
1
.def XH = r27
2
.def XL = r26
3
.def YH = r29
4
.def YL = r28
5
.def ZH = r31
6
.def ZL = r30
Deshalb ist geschickte Verwending von .undef notwendig, sonst kommt eine
Menge von Warning'en, wo man etwas Wichtige leicht übersehen kann:
in defs.inc:
1
.macro setr_a ; 1.Operand.
2
; fuer mehrere Byte, wo die Register einzeln zu behandeln sind
3
.set r0_a = 0
4
.set r1_a = 0
5
.set r2_a = 0
6
.set r3_a = 0
7
.set r4_a = 0
8
.set r5_a = 0
9
.set r6_a = 0
10
.set r7_a = 0
11
.set r8_a = 0
12
.set r9_a = 0
13
.set r10_a = 0
14
.set r11_a = 0
15
.set r12_a = 0
16
.set r13_a = 0
17
.set r14_a = 0
18
.set r15_a = 0
19
.set r16_a = 0
20
.set r17_a = 0
21
.set r18_a = 0
22
.set r19_a = 0
23
.set r20_a = 0
24
.set r21_a = 0
25
.set r22_a = 0
26
.set r23_a = 0
27
.set r24_a = 0
28
.set r25_a = 0
29
.set r26_a = 0
30
.set r27_a = 0
31
.set r28_a = 0
32
.set r29_a = 0
33
.set r30_a = 0
34
.set r31_a = 0
35
.set X_a = 0
36
.set xl_a = 0
37
.set xh_a = 0
38
.set Y_a = 0
39
.set yl_a = 0
40
.set yh_a = 0
41
.set Z_a = 0
42
.set zl_a = 0
43
.set zh_a = 0
44
.endm
45
46
.macro setr_b ; 2.Operand.
47
; fuer mehrere Byte, wo die Register einzeln zu behandeln sind
48
.set r0_b = 0
49
.set r1_b = 0
50
.set r2_b = 0
51
.set r3_b = 0
52
.set r4_b = 0
53
.set r5_b = 0
54
.set r6_b = 0
55
.set r7_b = 0
56
.set r8_b = 0
57
.set r9_b = 0
58
.set r10_b = 0
59
.set r11_b = 0
60
.set r12_b = 0
61
.set r13_b = 0
62
.set r14_b = 0
63
.set r15_b = 0
64
.set r16_b = 0
65
.set r17_b = 0
66
.set r18_b = 0
67
.set r19_b = 0
68
.set r20_b = 0
69
.set r21_b = 0
70
.set r22_b = 0
71
.set r23_b = 0
72
.set r24_b = 0
73
.set r25_b = 0
74
.set r26_b = 0
75
.set r27_b = 0
76
.set r28_b = 0
77
.set r29_b = 0
78
.set r30_b = 0
79
.set r31_b = 0
80
.set X_b = 0
81
.set xl_b = 0
82
.set xh_b = 0
83
.set Y_b = 0
84
.set yl_b = 0
85
.set yh_b = 0
86
.set Z_b = 0
87
.set zl_b = 0
88
.set zh_b = 0
89
.endm
90
91
.macro undefxz
92
.ifdef xl
93
.undef xl
94
.endif
95
.ifdef xh
96
.undef xh
97
.endif
98
.ifdef yl
99
.undef yl
100
.endif
101
.ifdef yh
102
.undef yh
103
.endif
104
.ifdef zl
105
.undef zl
106
.endif
107
.ifdef zh
108
.undef zh
109
.endif
110
.endm
111
112
.macro defrestore
113
114
.ifdef RA0
115
.undef RA0
116
.endif
117
.ifdef RA1
118
.undef RA1
119
.endif
120
.ifdef RA2
121
.undef RA2
122
.endif
123
.ifdef RA3
124
.undef RA3
125
.endif
126
.ifdef RA4
127
.undef RA4
128
.endif
129
.ifdef RA5
130
.undef RA5
131
.endif
132
.ifdef RA6
133
.undef RA6
134
.endif
135
.ifdef RA7
136
.undef RA7
137
.endif
138
139
.ifdef RB0
140
.undef RB0
141
.endif
142
.ifdef RB1
143
.undef RB1
144
.endif
145
.ifdef RB2
146
.undef RB2
147
.endif
148
.ifdef RB3
149
.undef RB3
150
.endif
151
.ifdef RB4
152
.undef RB4
153
.endif
154
.ifdef RB5
155
.undef RB5
156
.endif
157
.ifdef RB6
158
.undef RB6
159
.endif
160
.ifdef RB7
161
.undef RB7
162
.endif
163
164
.ifndef xl
165
.def xl = r26
166
.endif
167
.ifndef xh
168
.def xh = r27
169
.endif
170
.ifndef yl
171
.def yl = r28
172
.endif
173
.ifndef yh
174
.def yh = r29
175
.endif
176
.ifndef zl
177
.def zl = r30
178
.endif
179
.ifndef zh
180
.def zh = r31
181
.endif
182
.endm
183
184
.macro setreg_16a
185
.if r0_a
186
.def RA0 = r0
187
.def RA1 = r1
188
.elif r1_a
189
.def RA0 = r1
190
.def RA1 = r2
191
.elif r2_a
192
.def RA0 = r2
193
.def RA1 = r3
194
.elif r3_a
195
.def RA0 = r3
196
.def RA1 = r4
197
.elif r4_a
198
.def RA0 = r4
199
.def RA1 = r5
200
.elif r5_a
201
.def RA0 = r5
202
.def RA1 = r6
203
.elif r6_a
204
.def RA0 = r6
205
.def RA1 = r7
206
.elif r7_a
207
.def RA0 = r7
208
.def RA1 = r8
209
.elif r8_a
210
.def RA0 = r8
211
.def RA1 = r9
212
.elif r9_a
213
.def RA0 = r9
214
.def RA1 = r10
215
.elif r10_a
216
.def RA0 = r10
217
.def RA1 = r11
218
.elif r11_a
219
.def RA0 = r11
220
.def RA1 = r12
221
.elif r12_a
222
.def RA0 = r12
223
.def RA1 = r13
224
.elif r13_a
225
.def RA0 = r13
226
.def RA1 = r14
227
.elif r14_a
228
.def RA0 = r14
229
.def RA1 = r15
230
.elif r15_a
231
.def RA0 = r15
232
.def RA1 = r16
233
.elif r16_a
234
.def RA0 = r16
235
.def RA1 = r17
236
.elif r17_a
237
.def RA0 = r17
238
.def RA1 = r18
239
.elif r18_a
240
.def RA0 = r18
241
.def RA1 = r19
242
.elif r19_a
243
undefxz
244
.def RA0 = r19
245
.def RA1 = r20
246
.elif r20_a
247
undefxz
248
.def RA0 = r20
249
.def RA1 = r21
250
.elif r21_a
251
undefxz
252
.def RA0 = r21
253
.def RA1 = r22
254
.elif r22_a
255
undefxz
256
.def RA0 = r22
257
.def RA1 = r23
258
.elif r23_a
259
undefxz
260
.def RA0 = r23
261
.def RA1 = r24
262
.elif r24_a
263
undefxz
264
.def RA0 = r24
265
.def RA1 = r25
266
.elif r25_a
267
undefxz
268
.def RA0 = r25
269
.def RA1 = r26
270
.elif r26_a || X_a || xl_a
271
undefxz
272
.def RA0 = r26
273
.def RA1 = r27
274
.elif r27_a || xh_a
275
undefxz
276
.def RA0 = r27
277
.def RA1 = r28
278
.elif r28_a || Y_a || yl_a
279
undefxz
280
.def RA0 = r28
281
.def RA1 = r29
282
.elif r29_a || yh_a
283
undefxz
284
.def RA0 = r29
285
.def RA1 = r30
286
.elif r30_a || Z_a || zl_a
287
undefxz
288
.def RA0 = r30
289
.def RA1 = r31
290
.else
291
.error "Macro setreg_16a reg zu gross"
292
.endif
293
.endm
294
295
.macro setreg_16b
296
.if r0_b
297
.def RB0 = r0
298
.def RB1 = r1
299
.elif r1_b
300
.def RB0 = r1
301
.def RB1 = r2
302
.elif r2_b
303
.def RB0 = r2
304
.def RB1 = r3
305
.elif r3_b
306
.def RB0 = r3
307
.def RB1 = r4
308
.elif r4_b
309
.def RB0 = r4
310
.def RB1 = r5
311
.elif r5_b
312
.def RB0 = r5
313
.def RB1 = r6
314
.elif r6_b
315
.def RB0 = r6
316
.def RB1 = r7
317
.elif r7_b
318
.def RB0 = r7
319
.def RB1 = r8
320
.elif r8_b
321
.def RB0 = r8
322
.def RB1 = r9
323
.elif r9_b
324
.def RB0 = r9
325
.def RB1 = r10
326
.elif r10_b
327
.def RB0 = r10
328
.def RB1 = r11
329
.elif r11_b
330
.def RB0 = r11
331
.def RB1 = r12
332
.elif r12_b
333
.def RB0 = r12
334
.def RB1 = r13
335
.elif r13_b
336
.def RB0 = r13
337
.def RB1 = r14
338
.elif r14_b
339
.def RB0 = r14
340
.def RB1 = r15
341
.elif r15_b
342
.def RB0 = r15
343
.def RB1 = r16
344
.elif r16_b
345
.def RB0 = r16
346
.def RB1 = r17
347
.elif r17_b
348
.def RB0 = r17
349
.def RB1 = r18
350
.elif r18_b
351
.def RB0 = r18
352
.def RB1 = r19
353
.elif r19_b
354
undefxz
355
.def RB0 = r19
356
.def RB1 = r20
357
.elif r20_b
358
undefxz
359
.def RB0 = r20
360
.def RB1 = r21
361
.elif r21_b
362
undefxz
363
.def RB0 = r21
364
.def RB1 = r22
365
.elif r22_b
366
undefxz
367
.def RB0 = r22
368
.def RB1 = r23
369
.elif r23_b
370
undefxz
371
.def RB0 = r23
372
.def RB1 = r24
373
.elif r24_b
374
undefxz
375
.def RB0 = r24
376
.def RB1 = r25
377
.elif r25_b
378
undefxz
379
.def RB0 = r25
380
.def RB1 = r26
381
.elif r26_b || X_b || xl_b
382
undefxz
383
.def RB0 = r26
384
.def RB1 = r27
385
.elif r27_b || xh_b
386
undefxz
387
.def RB0 = r27
388
.def RB1 = r28
389
.elif r28_b || Y_b || yl_b
390
undefxz
391
.def RB0 = r28
392
.def RB1 = r29
393
.elif r29_b || yh_b
394
undefxz
395
.def RB0 = r29
396
.def RB1 = r30
397
.elif r30_b || Z_b || zl_b
398
undefxz
399
.def RB0 = r30
400
.def RB1 = r31
401
.else
402
.error "Macro setreg_16b reg zu gross"
403
.endif
404
.endm
Somit wird .macro movreg16 aus meiner vorigen Post so reduziert:
1
.macro mov16 ; @0,r1 r0-r30, 2c, 4b
2
3
setr_a
4
setr_b
5
.set @0_a = 1
6
.set @1_b = 1
7
.if REG16
8
setreg_16a
9
setreg_16b
10
mov RA0,RB0
11
mov RA1,RB1
12
.else
13
.error "Macro mov16 reg zu gross"
14
.endif
15
defrestore
16
.endm
Hier wird Warning etwa "reg.xx ist schon früher definiert" ausgegeben,
wenn RA und RB überschneiden, das finde ich gut.
Nun das Problem:
diese Variante funktioniert, wenn innerhalb Macro keine Macros verwendet
werden, die Unterscheiden zwischen oberen und unteren Register brauchen.
Wenn ich so etwas machen möchte:
, dann verliere ich in Macro ldireg definierte Unterschied zwischen
unteren und oberen Register. Alles wird compiliert etwa so:
aus ldireg16 r15,0x2512 wird:
1
ldi r16,0x12
2
mov r15,r16
3
ldi r16,0x25
4
mov r16,r16
Wie kann das verbessert werden???
P.S. Vollständigkeit halber: in früherer Post habe ich Macro OBEREREG
vergessen, anzugeben:
Makros dieser Komplexität sind einfach Murks. Wenn dann nur ganz
einfache. Oft haben sie schwer durchsichtige Haken. Hatte da auch mal
eine Makro-Empfehlung von c-hater (sorry, finds nicht mehr). Hatte
funktioniert aber in irgend einem Sonderfall plötzlich nicht mehr. Man
kann sich mit dem Zeug das Leben auch verdammt schwer machen. Ich glaube
es ist auch nicht 100%ig fehlerfrei im Assembler implementiert. Nehmt
doch besser gleich C oder was anderes.
c-hater schrieb:> Also, mal abgesehen davon, dass so ein Makro schon vom Ansatz her für> einen Assemblerprogrammierer ein Art Offenbarungseid darstellt
ist dagegen uneingeschränkt zuzustimnen.
AvrAsm schrieb:> Makros dieser Komplexität sind einfach Murks.
Besonders undurchsichtig wird es, wenn man dann noch Assemblermacros mit
Präprozessormacros (C-Style) wild durcheinander kombiniert.
Und natürlich läßt ein echter Assemblerknilch sämtliche Macros
unkommentiert, was sie machen und wie sie anzuwenden sind.
AVRASM2 erlaubt auch C-Präprozessormacros. Wenn diese Möglichkeit
existiert, sehe ich keine Gründe, darauf zu verzichten. Nur sollte man
merken, daß beide Macrosstyle miteinander kaum kombinierbar sind. Eines
geht besser mit C-Style, Anderes mit Assembler-Style.
Wegen fehlende Kommentar - ich dachte, alles ist zu offensichtlich, um
das noch kommentieren zu müssen. Wenn ich weiter Code poste, werde ich
mehr um Kommentare denken, danke für Hinweis.
Mein Anliegen ist, System aus Macros zu schaffen, um mit sozusagen
großen Blocks zu programmieren, ohne auf Vorteile Assemblers gegen C
verzichten zu müssen. Vieles davon habe ich schon gemacht, aber solange
alles nicht vollendet bleibt, möchte ich vorerst ganze Code nicht
veröffentlichen. Wenn jemand andere Meinung hat und lieber mit C lebt,
diese Möglichkeit bleibt für jeden sicher weiter offen. Hier geht es mir
nur darum, wie das besser zu machen ist. Nicht darum, ob ich das machen
sollte oder nicht.
Oder gibt es schon in Deutschland Assembler-Polizei? :)
Aber danke für Kommentare.
Ich sehe das Ganze wie quasi 64-bit-Befehlssatz, der etwa so wie in AVR
eingebauten Befehlssatz zu verwenden wäre. Deshalb meine Sorge um
einwandfreie Arbeit von Macros. Aber den Text von Macros selbst braucht
man dann nicht mehr zu sehen, so etwa wie bei Funktionen in C: wichtig
ist nur, was gegeben wird, was heraus kommt und was verändert wird, mehr
nicht.
Z.B. mit scheint es viel bequemer, ldireg r7,0x1234567898765432 zu
schreiben als
ldi r16,0x32
mov r7,r16
ldi r16,0x54
mov r8,r16 usw.
setr // 32 Variablen geschafft, je eine pro möglichen Reg, und auf 0 gesetzt
6
.set @0_mpr = 1 // Variable von denen auf 1 gesetzt, die @0 entspricht
7
.if OBEREREG_16 // falls eine von Vars 1 hat, die r16-r31 entsprechen
8
setr_a // neue Satz von Reg-Variablen gesetzt
9
.set @0_a = 1 // auch in diesem Satz entsprechende Var auf 1 gesetzt
10
setreg_16a // entsprechende Reg mittels .defs in Verbindung zu RA0,RA1 gebracht, dabei xl,xh,yl,yh,zl,zh mit .undef undefiniert
11
ldi RA0,low(@1) // eigentliche Handlung
12
ldi RA1,high(@1)
13
defrestore // RA0,RA1 undefiniert, xl,xh,yl,yh,zl,zh wieder definiert
14
.elif r15_mpr // falls Var gesetzt, die r15 entspricht
15
ldi temp,low(@1)
16
mov r15,temp
17
ldi r16,high(@1)
18
.elif UNTEREREG_16 // falls Vars gesetzt, die r0-r14 entsprechen
19
setr_a
20
.set @0_a = 1
21
setreg_16a
22
ldi temp,low(@1)
23
mov RA0,temp
24
ldi temp,high(@1)
25
mov RA1,temp
26
defrestore
27
.else // falls @0 nicht im Bereich r0-r30 liegt
28
.error "Macro ldireg16 reg zu gross"
29
.endif
30
.endif
31
.endm
Etwas aus der Sammlung von Macros habe ich aber schon lange getestet.
Hier möchte ich taktgenaue Verzögerung vorliegen, auf Idee von Klaus2m5,
https://www.mikrocontroller.net/articles/AVR_Assembler_Makros
Vielleicht wird das für jemand nützlich.
Maxim B. schrieb:> Z.B. mit scheint es viel bequemer, ldireg r7,0x1234567898765432 zu> schreiben als> ldi r16,0x32> mov r7,r16> ldi r16,0x54> mov r8,r16 usw.
Von den zusätzlichen Fehlermöglichkeiten, die man sich mit Makros
einhandelt einmal abgesehen, die Einführung zusätzlicher neuer
(Makro)Befehlsworte macht den Programmtext doch nur noch umfangreicher
und erklärungsbedürftiger. Vor allem aber intransparenter weil sich aus
einem ldireg nicht ohne auf den zugehörigen Asm-Code zu blicken
erschließt was da abläuft (das ist doch gerade bei Assembler wichtig!).
Dann kann ich den Asm-Code auch gleich hinschreiben. In Performance und
Speicherbedarf spart man ohnehin nichts, nur der Quelltext wird
entsprechend der Makro-Textersparnis länger. Dafür gewinnt man massiv an
Transparenz und Einfachheit.
Du kannst Dir ja gern Deine Makro-Sprache kreieren- der Schwerpunkt
dürfte dann aber auf "Deine" liegen und bleiben. Die erzielte
Textersparnis ist einfach zu teuer erkauft und kostet unnötig
Bürokratie, ohne nennenswertem Vorteil gegenüber Hochsprache, in der Du
ja gut und gerne auch Asm-Code einbetten kannst.
Für meine Begriffe verzettelst Du Dich mit diesem Vorhaben.
Nutze die Zeit z.B. lieber, um Dir standardisierte Zugriffsfunktionen
auf AVR Peripherie zu schreiben die sich schnell wiederverwenden lassen.
Davon hast Du langfristig viel mehr als tausend Makros für alles und
jedes.
AvrAsm schrieb:> Von den zusätzlichen Fehlermöglichkeiten, die man sich mit Makros> einhandelt einmal abgesehen, die Einführung zusätzlicher neuer> (Makro)Befehlsworte macht den Programmtext doch nur noch umfangreicher> und erklärungsbedürftiger. Vor allem aber intransparenter weil sich aus> einem ldireg nicht ohne auf den zugehörigen Asm-Code zu blicken> erschließt was da abläuft (das ist doch gerade bei Assembler wichtig!).
Ich sehe das anders. Makro mit sagekräfligen Namen minimiert mögliche
Fehler im Vergleich mit einzeln geschriebenen Befehlen. Da Hauptprogramm
dann deutlich kürzer geschrieben wird, wird alles übersichtlicher.
> Nutze die Zeit z.B. lieber, um Dir standardisierte Zugriffsfunktionen> auf AVR Peripherie zu schreiben
Das mache ich auch. Ich denke aber, am häufigsten werden
Datentausch-Befehle und Sprünge verwendet, deshalb denke ich vor allem
von ldi, mov und xch aller Art:
Reg <- Data
SRAM <- Data
Reg <-> Reg
SRAM <-> REG
EEPROM <-> Reg
EEPROM <-> SRAM
Reg <- FLASH
SRAM <- FLASH
T <-> Reg
T <-> SRAM
Usw.
PS. Gegebene macro_wait.inc setzt voraus, daß temp, templ, temph, temp0,
temp1, temp2, temp3 schon im Programm definiert sind (in macro_defs.inc)
Maxim B. schrieb:> minimiert mögliche> Fehler im Vergleich mit einzeln geschriebenen Befehlen
Was sind denn das für Fehler?
Doch nur solche die ein simples Build ohnehin anmahnt.
Maxim B. schrieb:> Makro mit sagekräfligen Namen
So aussagekräftig können Makro-Namen oft gar nicht sein wie sie sein
müssten!
Maxim B. schrieb:> dann deutlich kürzer geschrieben wird, wird alles übersichtliche
Du schaffst Dir ein Kuddelmuddel neuer Befehlsworte, zusätzlich zu den
ohnehin notwendigen Asm-Codes, von dem Du jetzt bei deren Erschaffung
glaubst, sie alle für immer in ihrer Funktionsweise im Kopf zu behalten.
In Deinem Kopf, denn für fremde ist das alles nur bedingt interessant.
Merkst Du nicht, daß Du hier ein Bürokratiemonster erschaffst?
Und dann schau sie Dir doch an, Deine komplexeren Makros.
Du tüftelst, investierst Zeit. Die wäre viel sinnvoller in eigentliche
Projekte gesteckt! Was Du in jedem Fall aber völlig unterschätzt ist das
zusätzliche Fehler-Potential "Kategorie heimtückisch" was Du Dir mit
Deiner Makro-Programmiererei an Land ziehst wenn Makros dann für
einzelne Fälle doch nicht so funktionieren wie erwartet. Das kann man
bei der ohnehin fehlerträchtigen Asm-Programmierung eigentlich gar nicht
gebrauchen.
AvrAsm schrieb:> So aussagekräftig können Makro-Namen oft gar nicht sein wie sie sein> müssten!
Man kann sehr einfach machen. Z.B. wenn schon sowieso mov reg-reg im
Befehlssatz vorhanden ist, liegt auf der Hand, daß mov16 ein
Registerpaar kopiert und mov64 schon 8 nebeneinander liegende Register.
mov kennt man schon sowieso.
So kann man bestehende Befelssatz aussagekräftig erweitern.
Oder als Beispiel, aus rol kann rol16 werden und aus asr asr16. Auch
fehlende Rotieren ohne C kann man ergänzen: rr, rl und entsprechend rr16
und rl16. Das habe ich schon gemacht.
xchregmem wird aussagekräftig für Tausch zwischen Register und Sram.
outreg ist aussagekräftig für Sram <- Reg...
AvrAsm schrieb:> Und dann schau sie Dir doch an, Deine komplexeren Makros.> Du tüftelst, investierst Zeit. Die wäre viel sinnvoller in eigentliche> Projekte gesteckt!
Wenn ich schon sowieso heute arbeitsfrei habe... Ich habe gestern genug
gespielt auf der Orgel, nun spiele ich auf dem Computer. Ganzes Leben
heißt Spiel :)
Maxim B. schrieb:> mov16 ein Registerpaar kopiert
mov16?
nimm doch movw!
> und mov64 schon 8 nebeneinander liegende> Register
noch nie benötigt
> aus rol kann rol16 werden und aus asr asr16. Auch> fehlende Rotieren ohne C kann man ergänzen: rr, rl und entsprechend rr16> und rl16
Gut das würde ich gerade noch einsehen.
Doch kommen solche Konstrukte in ohnehim meist nicht riesigen
Asm-Programmen oft nur 1mal vor, dann wär ein Makro schon wieder
überflüssig.
> xchregmem wird aussagekräftig für Tausch zwischen Register und Sram.> outreg ist aussagekräftig für Sram <- Reg...
Tausch? Outreg? Noch nie verwendet.Kein Bedarf. Entweder lds oder sts.
Zwar mit Einschränkung Reg>15 aber das muss kein Problem bedeuten.
Indexiertes Load/Store geht mit allen.
Maxim B. schrieb:> EEPROM <-> Reg> EEPROM <-> SRAM> Reg <- FLASH> SRAM <- FLASH
Ist z.T. Controller-abhängig.
Bei neueren AVRs machen es die in den Datenbereich gemappten EEPROM oder
Flash Bereiche einfach. Da brauchts die Makros nicht.
Muss Obrigkeit haben
Zoll, Steuern und Gaben.
Man weig're sich nicht
der schuldigen Pflicht! - hat J.S.Bach empfohlen.
Doch bleibet das Herze dem Höchsten alleine.
Nur Jedem das Seine.
AvrAsm schrieb:> Bei neueren AVRs machen es die in den Datenbereich gemappten EEPROM oder> Flash Bereiche einfach.
Zu moderne Sachen mag ich nicht. Zuerst sollte das mit der Zeit geprüft
werden... ATMega1284 und ATMega2560 sind für mich ausreichend.
Maxim B. schrieb:> Zu moderne Sachen mag ich nicht
Erneut:
"Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang"
Vielleicht realisiert das ja sogar ein Moderator?
Ansonsten - solche Monster-Makros macht nur, wer ein Verständnis-Problem
hat.
Hugo H. schrieb:> Ansonsten - solche Monster-Makros macht nur, wer ein Verständnis-Problem> hat.
Im konkreten Fall (also mit Betonung auf "solche"): ja. Im allgemeinen
Fall: nein.
Da können Makros auch einfach mal "der Ersatz" für das sein, was man in
C inline-Funktionen nennt. Und in diesem Einsatzfeld können sie auch
schonmal etwas größer geraten.
Der Vorteil von Assembler ist hier: ICH (und nicht ein völlig hirnloser
Compiler ohne jede Ahnung vom Gesamtkontext) entscheide, wann ge-inlined
wird. Allein dadurch, ob ich die Sache halt als Makro schreibe oder als
Subroutine.
I.d.R läuft's allerdings so, dass ich es wegen der einfacheren
Debugbarkeit zunächst als Subroutine schreibe, und es dann bei
entsprechender Dringlichkeit und Aufrufhäufigkeit in ein Makro umwandle.
Das ist zwar etwas mehr Schreibarbeit als in C eine Funktion zu einer
Inline-Funktion zu machen (wirklich nur etwas), aber ich kann dann
wenigstens zu 100% sicher sein, dass die Sache auch wirklich ge-inlined
wird!
c-hater schrieb:> Im konkreten Fall (also mit Betonung auf "solche"): ja. Im allgemeinen> Fall: nein.
Ich spare mir eine Antwort, die Du sowieso nicht hören möchtest :-)
Ich erinnere mich noch gut, als ich als blutiger Anfänger einen Z-80 mit
Assembler in die Hand bekam und frisch und unbedarft loslegte. Dann bin
ich auf die Makros gestossen und habe spontan gedacht: das isses... bis
ich herausfand, dass das Makro schlicht ein Name ist, den der Assembler
dann einfach durch den Code ersetzt. Da war die "Magie" weg und ist auch
nicht wiedergekehrt! Wenn man schon mit Assembler spielen möcht, dann
würde ich mir eine Sammlung Unterprogramme schaffen und die stetig
erweitern! (Was ich übrigens natürlich mache...)
Gruß Rainer
Rainer V. schrieb:> bis> ich herausfand, dass das Makro schlicht ein Name ist, den der Assembler> dann einfach durch den Code ersetzt.
Das genau ist, was auch ein Compiler grundsätzlich tut. Falls dir das
entgangen ist... All die schönen Kontrollstrukturen, die ganze
Mathematik, als dieses Zeug sind nix anderes als furchtbar komplizierte
Makros...
> Da war die "Magie" weg und ist auch> nicht wiedergekehrt! Wenn man schon mit Assembler spielen möcht, dann> würde ich mir eine Sammlung Unterprogramme schaffen und die stetig> erweitern!
Unterprogramme sind lästig. Der Callstack ist relativ teuer. Das ist oft
nicht akzeptabel für häufig benutzte Unterprogramme (weil es das
Programm langsam macht). Andererseits aber wiederum auch nicht für nur
einmalig benutzte (weil es das Programm unnötig vergrößert).
Der einzige, der sich unter weiser Abwägung aller Umstände wirklich
kompetent für eine optimale Wahl entscheiden kann, ist ein Mensch mit
Überblick über das Gesamtprogramm und die Umgebung, in der es läuft.
Kein normaler Compiler kann das leisten, ein JIT-Compiler richtiger
Hochsprachen kann es schon besser (weil er zumindest das Zielsystem
exakt kennt), aber im Allgemeinen ist der kompetente Mensch immer noch
die weitaus beste Wahl...
c-hater schrieb:> Unterprogramme sind lästig. Der Callstack ist relativ teuer. Das ist oft> nicht akzeptabel für häufig benutzte Unterprogramme (weil es das> Programm langsam macht).
Naja. Kommt schon drauf an wieviel wie wofür Register gesichert werden
müssen. In aller Regel fällt das genauso wenig wie
> einmalig benutzte (weil es das Programm unnötig vergrößert)
ins Gewicht, weil die Ressourcen dafür auch auf 8Bittern locker da sind.
Überlegen auf der Haben-Seite steht die gute Strukturierbarkeit und
Wiederverwendbarkeit von Code mit simpler Parameter-Über/Rückgabe. Der
fortgeschrittene Asm-Programmierer manipuliert darin sogar den
Stackpointer und gewinnt damit ein Code-Gestaltungsmittel über
Unterprogrammebenen hinweg.
AvrAsm schrieb:> Der> fortgeschrittene Asm-Programmierer manipuliert darin sogar den> Stackpointer und gewinnt damit ein Code-Gestaltungsmittel über> Unterprogrammebenen hinweg.
Der fortgeschrittene Asm-Programmierer bemüht sich, ohne Stack
auszukommen. Weil auch dessen Nutzung relativ teuer kommt.
c-hater schrieb:> relativ teuer
allenfalls in Programmen die an den Controller-Grenzen von Speicher und
Performance kratzen. Der Chip als solcher ist mit seinen Möglichkeiten
bereits bezahlt und darf gerne ohne irgendwelche teuren Rechnungen be-
und ausgenutzt werden. Da darf man sich auch (und gerade?) als
Asm-Programmierer den einen oder anderen Luxus gönnen.
c-hater schrieb:> Unterprogramme sind lästig. Der Callstack ist relativ teuer. Das ist oft> nicht akzeptabel für häufig benutzte Unterprogramme (weil es das> Programm langsam macht).
Du hast grundsätzlich Recht, aber ich habe in der Regel keine Haufen von
Unterprorammen, die in sich bis zum Abwinken verschachteln. Ich schreibe
natürlich auch kein Unterprogramm, dass mir z.B. Register tauscht oder
Ähnliches...das könnte dann natürlich auch ein Macro, aber das alles
führt jetzt zu umfangreichen "theoretischen" Überlegungen, die wir uns
hier nicht antun sollen. Pauschal gesagt, halte ich den Weg, den
Assembler mit Makros quasi hochsprachlich zu umkleiden, für unnötig bis
kontraproduktiv.
Gruß Rainer
Ich bin der Meinung, daß sowohl Macros wie auch Calls gut sein können,
je nachdem. Will man Flash sparen, so kann man Macro in Unterprogramm
umwandeln. Hat man genug Programmspeicher, so kann man etwas
Geschwindigkeit gewinnen... Immer besser wenn man Freihait hat, zwischen
Macro und Unterprogramm zu wählen. Wenn eine Menge von fertigen und
geprüften Macros bereit steht, kann man sehr leicht daraus
Unterprogramme für bestimmten Projekt machen.
Ich habe alle Muls und Divs bis 8 Bytes als Macro gemacht, aber es gibt
Nichts leicher als aus diesen Macros Unterprogramme zu machen. Wenn aber
bestimmte Mul oder Div im Programm nur einmal vorkommt, dann bleibt das
besser als Macro.
c-hater schrieb:> Unterprogramme sind lästig. Der Callstack ist relativ teuer. Das ist oft> nicht akzeptabel für häufig benutzte Unterprogramme (weil es das> Programm langsam macht). Andererseits aber wiederum auch nicht für nur> einmalig benutzte (weil es das Programm unnötig vergrößert).
Das sind genau 7 Clocks für rcall/ret plus 2 Byte für Stack und in
dieser function kann 1KByte Code sein oder mehr, absolut egal.
Aber ein Macro wird immer in voller Länge eingefügt.
Macros habe ich (vor langer Zeit) überwiegend benutzt, um Routinen
verständliche Namen zu geben z.B.
1
;*
2
.macro CmpReg2Ram ; Reg, RamAdr
3
lds MacReg, @1
4
cp @0, MacReg
5
.endmacro
6
;*
7
.macro CmpRam2Ram ; RamAdr1, RamAdr2
8
lds MacReg, @0
9
lds temp, @1
10
cp MacReg, temp
11
.endmacro
12
;*
Aber eine function zu schreiben, welche verschiedene Aufgaben erledigen
kann, abhängig von benutzten Argument, ist viel übersichtlicher und
spart enorm viel Code.
Man definiert benötigte Operationen als Konstanten mit sinnvollen Namen,
reserviert ein oder zwei Register dafür und ruft diese function auf.
So ähnlich waren CP/M und DOS aufgebaut und es hat fehlerlos
funktioniert (und tut es immer noch)...
U.A. bringen Macros auch solche Bequemlichkeit wie Übergabe von
Parameter. Sonst fehlt das in Assembler, so etwa wie auf C. Sonst muß
man in Assembler bestimmte Register selber futtern und erst dann Call.
Bei Macro aber @0,@1,@2,@3... So bequem!
Aus diesem Grund kann auch sinnvoll sein, einige Unterprogramme in Macro
ummanteln.
Maxim B. schrieb:> Sonst muß> man in Assembler bestimmte Register selber futtern und erst dann Call.> Bei Macro aber @0,@1,@2,@3... So bequem!
Ja, nur sind @0,@1... entweder feste Werte die dann auch wieder in
irgendwelche Register übernommen werden müssen, oder es sind die
Register selbst.
Nicht wirklich bequem!
Wie gesagt, Macros sind eher nützlich, um Routinen verständliche
Namen zu geben.
Hier ist noch ein Beispiel, welches ich geschrieben habe weil ich so
etwas oft benutzt habe und Geschwindigkeit wichtiger war als Länge:
Marc V. schrieb:> Ja, nur sind @0,@1... entweder feste Werte die dann auch wieder in> irgendwelche Register übernommen werden müssen, oder es sind die> Register selbst.
Oder auch Adressen in SRAM.
> Macros sind eher nützlich, um Routinen verständliche> Namen zu geben.
Oder um gut geprüfte Code zu verwenden ohne Gefahr, daß bei Tippen
irgendein Fehler kommt. Man kann beim Programmieren mit größeren Blocks
denken statt durch einzelne Assembler-Befehle das Ganze verlieren.
Maxim B. schrieb:> Man kann beim Programmieren mit größeren Blocks denken
... indem man gleich in Hochsprache programmiert ;-)
> Oder um gut geprüfte Code zu verwenden ohne Gefahr, daß bei Tippen> irgendein Fehler kommt.
Schon was von Copy&Paste gehört?
Sag mal, wieviele wirklich im Einsatz befindliche Programme hast Du
eigentlich schon 'makro'programmiert?
Peter D. schrieb:> Und natürlich läßt ein echter Assemblerknilch sämtliche Macros> unkommentiert, was sie machen und wie sie anzuwenden sind.
Ganz genau wie die typischen C-Knilche...
Man muss sich nur den dokumentierten Teil des Win32-API oder meinetwegen
auch des POSIX-API anschauen. In beiden gibt es absolut abenteuerliche
Makros, die sich nur erschließen mit einer wirklich gefestigten Kenntnis
des C-Präprozessors.
Das ist die typische dämliche, durch absolut nichts substantielles
begründete Arroganz der C-ler, ein wesentlicher Grund, warum ich C so
dermaßen hasse!
Der Punkt ist: Diese W****r gehen einfach davon aus, dass man C (incl.
Präprozessor) einfach umfassend zu beherrschen hat.
Du scheinst da mal erfahren zu haben, wie es aus der anderen Richtung
ausschaut. Das ist gut so! Heul' nicht! Mache ich doch auch nicht!
Lern' den Scheiß! Musste ich doch auch!
Marc V. schrieb:> Aber ein Macro wird immer in voller Länge eingefügt.
Speicherverschwendung pur.
Eine durchdachte Unterprogramm-Technik ist Makros da haushoch überlegen.
Marc V. schrieb:> Macros sind eher nützlich, um Routinen verständliche> Namen zu geben.
Kosmetik. Ein bischen Erklärung hinter einem Semikolon tuts mindestens
genauso.
c-hater schrieb:> Du scheinst da mal erfahren zu haben, wie es aus der anderen Richtung> ausschaut. Das ist gut so! Heul' nicht! Mache ich doch auch nicht!>> Lern' den Scheiß! Musste ich doch auch!
Ich glaube, du hast da an der falschen Adresse geschrien.
Er muß es bestimmt nicht lernen, höchstens sich wieder erinnern...
c-hater schrieb:> Das ist die typische dämliche, durch absolut nichts substantielles> begründete Arroganz der C-ler, ein wesentlicher Grund, warum ich C so> dermaßen hasse!
Das ist die typische dämliche, durch absolut nichts substantielles
begründete Arroganz der C-Leugner, ein wesentlicher Grund, warum ich
alles außer C so dermaßen hasse!
Oder so ähnlich.
c-hater schrieb:> Lern' den Scheiß! Musste ich doch auch!
Ich muß da überhaupt nichts lernen, die Assemblerzeiten sind definitv
vorbei. Ich hab keinerlei Schwierigkeiten, AVR-Assembler zu lesen, nur
erstellen werde ich ihn nicht mehr.
Und wenn man älter wird, merkt man, daß man Lebenszeit nicht sinnlos
verplempern sollte.
Beim Programmieren steht im Vordergrund, die gestellte Aufgabe möglichst
zeitnah, erweiterbar und fehlerfrei zu lösen. Nicht aber, einige wenige
Bytes und Zyklen zu sparen. Man will schließlich das Geld des
Auftraggebers nicht unnütz zum Fenster rausschmeißen.
Peter D. schrieb:> daß man Lebenszeit nicht sinnlos verplempern sollte
Das ist relativ zum Wissen und Können, Vorbereitung und Codefundus,
Anforderungen und Projektart, Vorlieben und Spaßfaktor...
> Nicht aber, einige wenige Bytes und Zyklen zu sparen.
So ist es. Heutige Controller bringen wahrlich genug mit, Engpässe
sollten da nicht so schnell auftreten. So verschwenderisch wie PC
Windows-Programme wird Controllerprogrammierung niemals werden :)
AvrAsm schrieb:> So verschwenderisch wie PC> Windows-Programme wird Controllerprogrammierung niemals werden :)
Da bin ich mir nicht mehr so sicher.
Hier laufen ja mehrfache Threads mit Java, Tasmota, Sonoff, Wemos usw.
Die gehen schon stramm in diese Richtung (4MB Flash, 96kB RAM).
Für Aufgaben (2* DS18B20, 1* ADC, 2* Relais), die schon ein ATtiny25 in
C leicht könnte. Nur bei der Smartphone Anbindung muß der ATtiny25
passen.
Peter D. schrieb:> Die gehen schon stramm in diese Richtung (4MB Flash, 96kB RAM)
Hatte da eher AVR & Co im Hinterkopf.
Peter D. schrieb:> die schon ein ATtiny25 in C leicht könnte
Ja. Eigentlich peinlich für die anderen.
Jimi H. schrieb:> Maxim B. schrieb:>> Man kann beim Programmieren mit größeren Blocks denken>> ... indem man gleich in Hochsprache programmiert ;-)
Leider ist mit Hochsprachen nicht alles schön...
Z.B. C-GCC hat für AVR double mit 4 Bytes! Will man wirklich genau
rechnen, so muß man andere Wege suchen... Mit Assembler kann man
problemlos beliebige Bytes multiplizieren und dividieren.
AvrAsm schrieb:> Eine durchdachte Unterprogramm-Technik ist Makros da haushoch überlegen.
Ist das wirklich klug, aus vielen Möglichkeiten manche freiwillig nicht
benutzen?
Maxim B. schrieb:> Ist das wirklich klug, aus vielen Möglichkeiten manche freiwillig nicht> benutzen?
Manche Möglichkeiten sind halt besser, manche schlechter.
Aber da wir hier schon festgestellt hatten daß es an Speicher selten
mangelt kannst Du Deiner Makro-Lust und der Kreation vieler vieler neuer
(Makro)Befehle ungestraft nachgehen. Verliere bloß nicht die Übersicht!
Derweil wähle ich aus meinem Vorrat an Unterprogrammen das Geeignete.
Maxim B. schrieb:> Will man wirklich genau> rechnen, so muß man andere Wege suchen...
In der Praxis mit 8Bittern will man hinreichend und nicht beliebig genau
rechnen. Dafür reicht C völlig. Steigen die Anforderungen ans Rechnen
kommt statt Assembler meist bessere Hardware zum Einsatz. Offensichtlich
fehlt es Dir an Erfahrungen mit realen Projekten. Du spielst doch nur,
oder?
Maxim B. schrieb:> Z.B. C-GCC hat für AVR double mit 4 Bytes! Will man wirklich genau> rechnen, so muß man andere Wege suchen... Mit Assembler kann man> problemlos beliebige Bytes multiplizieren und dividieren.
Du hast ne 64Bit double-Lib in Assembler. Cool.
Falls man nur die Auflösung, aber keine große Dynamik braucht, kann man
unter AVR-GCC auch int64_t benutzen. Die Winkelfunktionen fehlen dann
natürlich.
Der AVR-IAR kann auch 64Bit double.
Peter D. schrieb:
> Du hast ne 64Bit double-Lib in Assembler. Cool.
Ich möchte gerne eine hauseigene Variante mit 5 oder 6 Bytes machen. Für
Exponent reicht, denke ich, 1 Byte wie auch bei Float, Mantisse möchte
ich aber gerne mindestens 4 Bytes haben. Z.B. Exponent 9 bit, Vorzeichen
1 bit und Mantisse 30 oder 38 bit. Double scheint mir für Praxis zu
groß, Float zu ungenau, so etwas dazwischen. Leider reichen mir
Kenntnisse dafür noch nicht. Aber ich hoffe, ich lerne das. Ich hoffe,
ich finde Algorithmen.
Wenn ich mit macro_basic.inc fertig bin, versuche ich in dieser Richtung
zu gehen.
Im Moment beschäftige ich mich mit der Frage: bei einigen Operationen
ist unausweichlich, temp-Register zu nutzen. Aber temp als zusätzliche
Argument immer schreiben zu müssen, das ist zu aufwendig. Trotzdem wäre
es gut, temp-Register nach Bedarf zu ändern. Das erreiche ich mit .set
und ein paar Macros in macro_defs.inc. Problem ist nur, wie genau das zu
machen wäre. Bei einigen Macro kann ich auf gleiche Weise wie bei
Register gehen:
; Hilfsmacro
.macro andiu ; @0 r0-r31, @1 Data8, 2c, 4b
; um work-Register frei definieren zu koennen
; Wichtig: Macro sollte innerhalb Macro verwendet werden,
; wo am Ende macro defrestore steht.
defreg_16w ; RB0 an bestimmte Register in Abhängigkeit von Variablen
r16_w - r31_w bestimmt
ldi RB0,@1
and @0,RB0
.endm
Leider arbeitet das ohne Warning nicht überall. Bei ldireg klappt das
nicht, ich muß auf die "Treppe" gehen, die zwar einwandfrei
funktioniert, aber zu groß aussieht:
; Hilfsmacro
.macro ldiu ; @0 r0-r31, @1 Data8, work benutzt 2c, 4b
; um work-Register frei definieren zu koennen.
.if r17_w
ldi r17,@1
mov @0,r17
.elif r18_w
ldi r18,@1
mov @0,r18
.elif r19_w
ldi r19,@1
mov @0,r19
.elif r20_w
ldi r20,@1
mov @0,r20
.elif r21_w
ldi r21,@1
mov @0,r21
.elif r22_w
ldi r22,@1
mov @0,r22
.elif r23_w
ldi r23,@1
mov @0,r23
.elif r24_w
ldi r24,@1
mov @0,r24
.elif r25_w
ldi r25,@1
mov @0,r25
.elif r26_w
ldi r26,@1
mov @0,r26
.elif r27_w
ldi r27,@1
mov @0,r27
.elif r28_w
ldi r28,@1
mov @0,r28
.elif r29_w
ldi r29,@1
mov @0,r29
.elif r30_w
ldi r30,@1
mov @0,r30
.elif r31_w
ldi r31,@1
mov @0,r31
.else
ldi r16,@1
mov @0,r16
.endif
.endm
Ich möchte gerne verstehen, warum das so ist: manchmal funktioniert und
manchmal nicht.
Peter D. schrieb:
> Der AVR-IAR kann auch 64Bit double.
So viel Geld habe ich leider nicht... Billigere eingeschränkten
Versionen sind für mich keine Option: schon selbst das Wort "Grenze"
bringt mich in Wut.
Peter D. schrieb:
> Die Winkelfunktionen fehlen dann natürlich.
Diese Algorithmen möchte ich gerne auch verstehen lernen. Mit mul und
div bis 64x64 habe ich schon das Problem in Assembler gelöst. Dabei ging
ich bei größeren divs und muls schon an die Grenze, sie als Macros zu
schreiben. Ich denke, die Schreibweise als Macro ist für kleinere
Argumente hilfreich. Bei den größeren, die nicht so häufig in Programm
vorkommen und auch viel Zeit brauchen, ist auch die Schreibweise als
Funktion berechtigt.
Wahrscheinlich wäre es sinnvoller die Argumenten und Ergebnis nicht wie
bei GCC über Register zu geben, sondern über SRAM. Z.B. über (X),(Y) und
(Z). Mit Rücksicht auf solche Möglichkeit habe ich in meine
macro_defs.inc die Befehle xch8 - xch64 (REG <-> REG, auf eor - Basis)
und xchregmem8 - xchregmem64 (REG <-> IO-RAM) eingeführt.
Maxim B. schrieb:> Aber temp als zusätzliche> Argument immer schreiben zu müssen, das ist zu aufwendig.
Wozu soll sowas nötig sein?
Ich reserviere für temp und für die Operanden immer die gleichen
Symbole. Sie sind als Scratchpad reserviert, d.h. dürfen zerstört
werden. Also brauchen sie nicht gepust/popt werden.
Da ich nie die Register direkt verwende, sondern nur die Symbole, können
in anderen Projekten natürlich andere Register als t2..0 usw. definiert
werden. Ich könnte mir eh nicht merken, welche Funktion z.B. r16 im
Projekt gerade hat.
Hier mal die Registermap für ein Projekt:
Peter D. schrieb:> Wozu soll sowas nötig sein?> Ich reserviere für temp und für die Operanden immer die gleichen> Symbole. Sie sind als Scratchpad reserviert, d.h. dürfen zerstört> werden. Also brauchen sie nicht gepust/popt werden.
Im Prinzip ja. Ich möchte aber Macros wirklich universell machen. Wenn
nichts angegeben wird, wird ja als work r16 und als puffer r0 genommen
(für Operationen etwa wie xch reg-mem und mem-mem, da mit in, out, lds
und sts alle Reg arbeiten und nicht nur die oberen). Aber eine
Möglichkeit zu haben ist ja besser, als nicht zu haben?
Am liebsten würde ich so machen, daß diese Macros von außen gar nichts
außer Register für Operanden verwenden, aber das würde zu viel Unnötiges
bedeuten (push-pop, die man lieber für ganzen Block einmal macht).
Deshalb schaffe ich die Möglichkeit, als work und puffer die Register zu
wählen, die im Moment an wenigsten an Bedeutung haben. Das ist etwa wie
in Orchesterpartitur: nach einem Altschlüssel bedeutet eine Note auf der
dritten Linie immer c1, dann schreiben wir Sopranschlüssel - und für
alles danach ist gleiche Note schon g1! Dann Baßschlüssel - und das
Gleiche heißt schon d0! So wirken auch Macros work16-work31.
Normalerweise wird bei mir r16 als temp verwendet. Aber es gibt ab und
zu Umstände, wo Register möglichst ungebrochen nacheinander verwendet
sein sollten, dann paßt als temp r31 besser. Aber normalerweise ist r31
als zh zu wert, um als temp benutzt zu werden... Also, es gibt
verschiedene Umstände...
macro_basic.inc und macro_defs.inc kann ich einfach zu jedem Projekt
eingliedern, die brauchen sozusagen weder essen noch trinken: solange
Macro nicht werwendet wird, wird auch Flash nicht verbraucht.
Warum werd ich das Gefühl nicht los hier wird ein Pferd von hinten
aufgezäumt? Daß hier fortwährend verschlimmbessert und
behelfskonstruiert werden muß?
Programmiere ja selber in Asm aber hier blickt wohl nur noch der Autor
durch.
Maxim B. schrieb:> macro_basic.inc und macro_defs.inc kann ich einfach zu jedem Projekt> eingliedern
Keinesfalls. Es nimmt auf jeden Fall ettliche Namen in Anspruch.
> solange Macro nicht werwendet wird, wird auch Flash nicht verbraucht.
Das immerhin fand ich tröstlich.
Peter D. schrieb:> Sie sind als Scratchpad reserviert, d.h. dürfen zerstört werden. Also> brauchen sie nicht gepust/popt werden.
Das ändert doch auch nichts an der Einschränkung daß die verwendeten
Register dann in Interrupts nicht verwendet werden dürfen?
Läuft die durchgängige Festlegung auf bestimmte Übergabe-Parameter in
allen Routinen nur auf konkretbestimmte Register r0-r31 nicht aufs
Gleiche hinaus? Dann kann der Register .def Teil nämlich wegfallen.
Zuviel vorab festgelegte Register- Funktion schränkt doch nur ein,
benannte Register mögen zwar hübsch ausschaun aber die eigentliche
Funktionsweise wird wie bei Makros verdeckt: Einem Namen sehe ich nicht
mehr ohne weiteres an ob er in einem konkreten Zusammenhang verwendet
werden kann (r10= genauso vielsagend wie t0 z.B. nicht für lds).
Uwe schrieb:> Warum werd ich das Gefühl nicht los hier wird ein Pferd von hinten> aufgezäumt? Daß hier fortwährend verschlimmbessert und> behelfskonstruiert werden muß?
Wenn du ein Problem mit dem Pferd hast... Wie kann ich dir helfen? Lese
mal was Anderes vielleicht...
Einige Forumteilnehmer verwechseln oft zwei Fragen: statt "wie macht man
das besser" beginnen sie eine völlig andere Frage zu beantworten: "warum
man so und so nicht machen darf".
Komisch... Scheinbar erwachsene Menschen... Oder?
Umso mehr schätze ich Menschen, die die wirklich gestellte Fragen
beantworten.
Maxim B. schrieb:> Umso mehr schätze ich Menschen, die die wirklich gestellte Fragen> beantworten.
Du weißt aber auch, dass es alle Sorten von Fragern gibt und nicht
wenige von denen haben ihre Frage nicht richtig gestellt oder nur
stückchenweise formuliert oder sogar gar nicht verstanden...Hier ist das
Thema zwar interessant und auch klar...trotzdem sind beide Wege in etwa
gleichwertig, sodass hier auch keine Hilfe gefragt ist...eher halt
"Glaubensbekenntnisse". Tiefer sollte das auch nicht gehen...sonst
finden wir garantiert kein Ende!
Gruß Rainer
Maxim B. schrieb:> Wenn du ein Problem mit dem Pferd hast... Wie kann ich dir helfen?
Nein Maxim Du brauchst Hilfe weil Du gerade nicht erkennst daß nicht das
Pferd selber das Problem war.
> statt "wie macht man> das besser"
Genau das wird Dir doch beantwortet: Nicht mit Makros!
> Umso mehr schätze ich Menschen, die die wirklich gestellte Fragen> beantworten.
Nein Du willst sagen umso mehr schätzt Du Menschen die Dich in Deiner
Makro-Verliebtheit bestätigen.
Bürokratie mit Bürokratie zu bekämpfen mag ja vom Anliegen her ehrenwert
sein, bedeutet aber in der Praxis nur noch mehr Bürokratie. Aus
mutmaßlichem Vereinfachen wird Verkomplizieren. Das Thema ist vor allem
deshalb so sensibel weil man sich, gerade mit den komplexeren Makros,
hier völlig unnötig eine neue Kategorie Fehler ins Boot einer
Programmierung holt, die für sich schon anspruchsvoll genug ist.
AvrAsm schrieb:> Nein Maxim Du brauchst Hilfe weil Du gerade nicht erkennst daß nicht das> Pferd selber das Problem war.
Leider verstehe ich es mit Pferden wenig. Wenn es um Orgeln gehe, könnte
ich dir mehr helfen...
Wer sich dafür entscheidet, den Rat von Leuten zu ignorieren, die über
Expertise in einer Sache verfügen, muss halt seine eigenen Erfahrungen
machen. Das gilt ex aequo auf den Gebieten der Unpaarhufer, der
Kirchenmusik, und dem Assemblerprogrammieren.
Was Dir hier zu dem Thema gesagt wurde, entspricht der Wahrheit. In den
Erklärungen steckt ne Menge Wissen drin - alles gratis für Dich. Jetzt
glauben oder später checken? Tipp: Die erste Option ist zeitsparender
:-)
Just my two cents...
Und "Glaubensbekenntnis"...ich denke, bei den meisten
Assemblerprogrammierern handelt es sich wohl doch nur um ambitionierte
Bastler. Wer regelmäßig gezwungen ist, auf Assemblerebene
"runterzusteigen", für den stellt sich das Problem gar nicht. Und zwar,
weil es kein Problem ist, weil dieser Jemand das einfach "so" macht, wie
er es schon immer macht...da wird im Leben keiner kommen und schreien,
"warum haste denn da nicht ein Macro definiert, du Depp du"
Gruß Rainer
Die Compilerbauer sind keine Dummbatzen, sondern haben mehr Ahnung von
Assembler als jeder von uns. Wenn die bestimmte Register als Scratchpad
und für die Argumentenübergabe festlegen, dann weil es Vorteile hat
(kurzer und schneller Code). Das ist auch keine neue Technik beim AVR,
sondern bekannt, z.B. der Herr Keil hat das beim 8051 auch so gemacht.
Nun bin ich aber kein Compiler, der bei jedem neuen Durchlauf einen
Call-Tree aufbauen kann und den vollen Überblick über die
Registerbenutzung behält. Daher der Trick, daß ich je Projekt in einer
Datei global die Registerverwendung definiere. So kann ich schnell
Konflikte feststellen und bei Bedarf Variablen in dem RAM verschieben.
Nur bei den Befehlen, die Register fest verwenden (LPM, SPM, MUL),
schreibe ich direkt r0, r1 in den Code. Alle anderen Register werden nur
über Symbole zugegriffen, So kann mir keine fehlerhafte Doppelbenutzung
passieren.
Lange Rede kurzer Sinn, die Register in jeder Funktion umdefinieren zu
können und statt aussagefähiger Namen immer nur die Nummern 0..31 zu
verwenden, ist Quatsch und führt ins Chaos, sobald die Projekte etwas
größer werden.
Es gibt auch überhaupt keinen Grund, für die Rechenfunktionen ständig
wechselnde Register zu verwenden. Will man sie in einer 2. Instanz
(Interrupt) verwenden, muß man sie eben sichern. Abgesehen davon haben
langdauernde Berechnungen in Interrupts eh nichst verloren.
Peter D. schrieb:> die Register in jeder Funktion umdefinieren zu> können und statt aussagefähiger Namen immer nur die Nummern 0..31 zu> verwenden, ist Quatsch und führt ins Chaos, sobald die Projekte etwas> größer werden
Keineswegs Peter.
Mit ein wenig Systematik bei der Vergabe ausgesuchter
Register-Funktionen werden geordnete Bahnen nicht verlassen. Wenn für
bestimmte wiederkehrende Funktionen immer dieselben Register verwendet
werden ist es egal, ob sie nun r16 oder TEMP, r10 oder t0 heißen.
Umgekehrt kann ich r16 im gleichen Programm dann auch mal nicht als
TEMP verwenden und lege mich so nicht vornherein auf eine Funktion fest.
Und wie schon gesagt bleiben spezifische Eigenschaften der Register am
besten vor Augen wenn diese mit ihrem originalen Namen angesprochen
werden.
Wir reden hier übrigens nicht von Compiler-Bau sondern normalen
Projekten, meist mit einem Haupt- und Interruptprogrammanteil, woraus
ganz natürlich gewisse Grundfunktionalitäten folgen.
Umbenennungen sind im wesentlichen Philosophie und Geschmack des
einzelnen Asm-Programmierers überlassen. Kleinere, überschaubare
Programme bieten sich dafür eher an als größere, vor allem dann, wenn
für wichtige Speicheroperationen der Registerspeicher vorgesehen ist
oder aber der Code für möglichst viele Betrachter verständlich gemacht
werden soll. Alles in allem würde ich sagen, das Optimum liegt irgendwo
in der Mitte. Nichts zu benennen als dem einen, alles zu benennen als
dem anderen Extrem. Doch ganz egal wie sich der Einzelne entscheidet:
Die Freiheit der Gestaltung macht einen großen Reiz der Assemblersprache
aus. Nur bei der Anzahl der verwendeten Namen und Codewörter würd ich
behaupten ist weniger oft mehr.
Peter D. schrieb:> Die Compilerbauer sind keine Dummbatzen, sondern haben mehr Ahnung von> Assembler als jeder von uns. Wenn die bestimmte Register als Scratchpad> und für die Argumentenübergabe festlegen, dann weil es Vorteile hat> (kurzer und schneller Code).
Ich habe in avr-libc gekuckt: dort sind auch Assembler-Macros benutzt,
um Register zu redefinieren und um Registername als Zahl-Parameter zu
benutzen. Das ist zwar anders gemacht, da auch Syntax von GCC-Assembler
im Vergleich mit AVRASM2 anders ist.
"All expressions are internally 32 bits in AVRASM, and 64 bits in
AVRASM2. " - somit hat AVRASM2 bestimmte Bequemlichkeiten, die
GCC-Assembler nicht kennt. GCC-Assembler ist aber notwendig, um
Funktionen zu schreiben, die in C benutzt werden.
Peter D. schrieb:> Daher der Trick, daß ich je Projekt in einer> Datei global die Registerverwendung definiere. So kann ich schnell> Konflikte feststellen und bei Bedarf Variablen in dem RAM verschieben.
Hier kommt bestimmt ein Mißverständnis. Die Möglichkeit, in meinen
Macros Arbeitsregister wählen zu können, betrifft NUR internen Gebrauch
von Register in Macros. Diese Möglichkeit ist gerade nützlich, um
mögliche Konflikte zwischen Macro und Temp-Register von Nutzer zu
vermeiden. Will man Nutzer-temp ändern, sollte man in keinem Fall
interne Mittel von Macrolib nehmen, sondern mit #define wie üblich. Im
Idealfall sollten Macro-interne Arbeitsregister unsichtbar bleiben,
leider ist das nicht immer möglich. Deshalb kann man so vorgehen: will
Nutzer z.B. als temp r16 wählen, das stillschweigend intern in Macros
als work und als puffer benutzt wird - so kann der Nutzer seine
Lieblingsregister weiter als temp nehmen, und für Macros werden z.B. mit
work20 und puffer3 andere Register für internen Gebrauch genommen.
Übrigens, Konflikte zwischen Macro-Arbeitsregister und Nutzer-temp sind
nur bei bestimmten Gebrauch von temp möglich. Man kann diese Konflikte
auch leicht vermeiden. Dann braucht man nichts ändern, diese Möglichkeit
bleibt ungenutzt, sie kostet ja auch nichts.