Forum: Mikrocontroller und Digitale Elektronik AVR Assembler, Macro, Conditional mit Register


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Horst M. (horst)


Lesenswert?

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?

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Welcher Assembler?

von Horst M. (horst)


Lesenswert?

(prx) A. K. schrieb:
> Welcher Assembler?

AVRASM: AVR macro assembler 2.1.42

von (prx) A. K. (prx)


Lesenswert?

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

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

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:

1
.macro fetch_byte
2
  .set r16_macroprobe = 0
3
  .set @0_macroprobe = 1
4
  .if r16_macroprobe
5
    rcall read_byte
6
  .else
7
    push r16
8
    rcall read_byte
9
    mov @0,r16
10
    pop r16
11
  .endif
12
.endm

von Peter D. (peda)


Lesenswert?

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.

von Horst M. (horst)


Lesenswert?

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

von Maxim B. (max182)


Lesenswert?

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
31
  .set r0_mpr = 0
32
  .set r1_mpr = 0
33
  .set r2_mpr = 0
34
  .set r3_mpr = 0
35
  .set r4_mpr = 0
36
  .set r5_mpr = 0
37
  .set r6_mpr = 0
38
  .set r7_mpr = 0
39
  .set r8_mpr = 0
40
  .set r9_mpr = 0
41
  .set r10_mpr = 0
42
  .set r11_mpr = 0
43
  .set r12_mpr = 0
44
  .set r13_mpr = 0
45
  .set r14_mpr = 0
46
  .set r15_mpr = 0
47
  .set r16_mpr = 0
48
  .set r17_mpr = 0
49
  .set r18_mpr = 0
50
  .set r19_mpr = 0
51
  .set r20_mpr = 0
52
  .set r21_mpr = 0
53
  .set r22_mpr = 0
54
  .set r23_mpr = 0
55
  .set r24_mpr = 0
56
  .set r25_mpr = 0
57
  .set r26_mpr = 0
58
  .set r27_mpr = 0
59
  .set r28_mpr = 0
60
  .set r29_mpr = 0
61
  .set r30_mpr = 0
62
  .set r31_mpr = 0
63
  .set X_mpr = 0
64
  .set xl_mpr = 0
65
  .set xh_mpr = 0
66
  .set Y_mpr = 0
67
  .set yl_mpr = 0
68
  .set yh_mpr = 0
69
  .set Z_mpr = 0
70
  .set zl_mpr = 0
71
  .set zh_mpr = 0
72
.endm
73
#endif /* _SETR_ */

Somit ist nun folgende Schreibweise möglich:
1
.macro ldireg  ; @0 r0-r31, @1 Data8, 1-2c, 2-4b, wenn r0-r15, temp benutzt
2
  .if @1 > 0xff
3
    .error "ldireg die Zahl ist zu gross"
4
  .else
5
  setr16
6
  .set @0_mpr = 1
7
  .if OBEREREG
8
    ldi @0,@1
9
  .else
10
    ldi temp,@1
11
    mov @0,temp
12
  .endif
13
  .endif
14
.endm

Aber Versuch, nach solcher Art eine Operation mit mehreren Bytes zu 
machen, bringt ziemlich aufwendige Code:
1
.macro ldireg16  ; @0 r0-r30, @1 Data16, 2-4c, 4-8b, wenn r0-r15, temp benutzt
2
  .if @1 > 0xffff
3
    .error "ldireg16 die Zahl ist zu gross"
4
  .else
5
  setr
6
  .set @0_mpr = 1
7
  .if r0_mpr
8
    ldireg r0,low(@1)
9
    ldireg r1,high(@1)
10
  .elif r1_mpr
11
    ldireg r1,low(@1)
12
    ldireg r2,high(@1)
13
  .elif r2_mpr
14
    ldireg r2,low(@1)
15
    ldireg r3,high(@1)
16
  .elif r3_mpr
17
    ldireg r3,low(@1)
18
    ldireg r4,high(@1)
19
  .elif r4_mpr
20
    ldireg r4,low(@1)
21
    ldireg r5,high(@1)
22
  .elif r5_mpr
23
    ldireg r5,low(@1)
24
    ldireg r6,high(@1)
25
  .elif r6_mpr
26
    ldireg r6,low(@1)
27
    ldireg r7,high(@1)
28
  .elif r7_mpr
29
    ldireg r7,low(@1)
30
    ldireg r8,high(@1)
31
  .elif r8_mpr
32
    ldireg r8,low(@1)
33
    ldireg r9,high(@1)
34
  .elif r9_mpr
35
    ldireg r9,low(@1)
36
    ldireg r10,high(@1)
37
  .elif r10_mpr
38
    ldireg r10,low(@1)
39
    ldireg r11,high(@1)
40
  .elif r11_mpr
41
    ldireg r11,low(@1)
42
    ldireg r12,high(@1)
43
  .elif r12_mpr
44
    ldireg r12,low(@1)
45
    ldireg r13,high(@1)
46
  .elif r13_mpr
47
    ldireg r13,low(@1)
48
    ldireg r14,high(@1)
49
  .elif r14_mpr
50
    ldireg r14,low(@1)
51
    ldireg r15,high(@1)
52
  .elif r15_mpr
53
    ldireg r15,low(@1)
54
    ldireg r16,high(@1)
55
  .elif r16_mpr
56
    ldireg r16,low(@1)
57
    ldireg r17,high(@1)
58
  .elif r17_mpr
59
    ldireg r17,low(@1)
60
    ldireg r18,high(@1)
61
  .elif r18_mpr
62
    ldireg r18,low(@1)
63
    ldireg r19,high(@1)
64
  .elif r19_mpr
65
    ldireg r19,low(@1)
66
    ldireg r20,high(@1)
67
  .elif r20_mpr
68
    ldireg r20,low(@1)
69
    ldireg r21,high(@1)
70
  .elif r21_mpr
71
    ldireg r21,low(@1)
72
    ldireg r22,high(@1)
73
  .elif r22_mpr
74
    ldireg r22,low(@1)
75
    ldireg r23,high(@1)
76
  .elif r23_mpr
77
    ldireg r23,low(@1)
78
    ldireg r24,high(@1)
79
  .elif r24_mpr
80
    ldireg r24,low(@1)
81
    ldireg r25,high(@1)
82
  .elif r25_mpr
83
    ldireg r25,low(@1)
84
    ldireg r26,high(@1)
85
  .elif r26_mpr || X_mpr || xl_mpr
86
    ldireg r26,low(@1)
87
    ldireg r27,high(@1)
88
  .elif r27_mpr || xh_mpr
89
    ldireg r27,low(@1)
90
    ldireg r28,high(@1)
91
  .elif r28_mpr || Y_mpr || yl_mpr
92
    ldireg r28,low(@1)
93
    ldireg r29,high(@1)
94
  .elif r29_mpr || yh_mpr
95
    ldireg r29,low(@1)
96
    ldireg r30,high(@1)
97
  .elif r30_mpr || Z_mpr || zl_mpr
98
    ldireg r30,low(@1)
99
    ldireg r31,high(@1)
100
  .else
101
    .error "Macro ldireg16 reg zu gross"
102
  .endif
103
  .endif
104
.endm

Noch schlimmer wird bei einer Mehrbyte-Operation mit zwei Register:
1
.macro movreg16  ; @0 r0-r30, @1 r0-r30  1-2c,2-4b
2
  setr
3
  .set @0_mpr = 1
4
  .if r0_mpr
5
    setrdop
6
    .set @1_dop = 1
7
    .if r0_dop
8
      movw r1:r0,r1:r0
9
    .elif r1_dop
10
      .error "Macro movreg16 @0 ueber @1"
11
    .elif r2_dop
12
      movw r1:r0,r3:r2
13
    .elif r3_dop
14
      mov r0,r3
15
      mov r1,r4
16
    .elif r4_dop
17
      movw r1:r0,r5:r4
18
    .elif r5_dop
19
      mov r0,r5
20
      mov r1,r6
21
    .elif r6_dop
22
      movw r1:r0,r7:r6
23
    .elif r7_dop
24
      mov r0,r7
25
      mov r1,r8
26
    .elif r8_dop
27
      movw r1:r0,r9:r8
28
    .elif r9_dop
29
      mov r0,r9
30
      mov r1,r10
31
    .elif r10_dop
32
      movw r1:r0,r11:r10
33
    .elif r11_dop
34
      mov r0,r11
35
      mov r1,r12
36
    .elif r12_dop
37
      movw r1:r0,r13:r12
38
    .elif r13_dop
39
      mov r0,r13
40
      mov r1,r14
41
    .elif r14_dop
42
      movw r1:r0,r15:r14
43
    .elif r15_dop
44
      mov r0,r15
45
      mov r1,r16
46
    .elif r16_dop
47
      movw r1:r0,r17:r16
48
    .elif r17_dop
49
      mov r0,r17
50
      mov r1,r18
51
    .elif r18_dop
52
      movw r1:r0,r19:r18
53
    .elif r19_dop
54
      mov r0,r19
55
      mov r1,r20
56
    .elif r20_dop
57
      movw r1:r0,r21:r20
58
    .elif r21_dop
59
      mov r0,r21
60
      mov r1,r22
61
    .elif r22_dop
62
      movw r1:r0,r23:r22
63
    .elif r23_dop
64
      mov r0,r23
65
      mov r1,r24
66
    .elif r24_dop
67
      movw r1:r0,r25:r24
68
    .elif r25_dop
69
      mov r0,r25
70
      mov r1,r26
71
    .elif r26_dop || X_dop || xl_dop
72
      movw r1:r0,r27:r26
73
    .elif r27_dop || xh_dop
74
      mov r0,r27
75
      mov r1,r28
76
    .elif r28_dop || Y_dop || yl_dop
77
      movw r1:r0,r29:r28
78
    .elif r29_dop || yh_dop
79
      mov r0,r29
80
      mov r1,r30
81
    .elif r30_dop || Z_dop || zl_dop
82
      movw r1:r0,r31:r30
83
    .else
84
      .error "Macro movreg16 2 reg zu gross"
85
    .endif
86
  .elif r1_mpr
87
    setrdop
88
    .set @1_dop = 1
89
    .if r0_dop || r2_dop
90
      .error "Macro movreg16 @0 ueber @1"
91
    .elif r1_dop
92
      mov r1,r1
93
      mov r2,r2
94
    .elif r3_dop
95
      mov r1,r3
96
      mov r2,r4
97
    .elif r4_dop
98
      mov r1,r4
99
      mov r2,r5
100
    .elif r5_dop
101
      mov r1,r5
102
      mov r2,r6
103
    .elif r6_dop
104
      mov r1,r6
105
      mov r2,r7
106
    .elif r7_dop
107
      mov r1,r7
108
      mov r2,r8
109
    .elif r8_dop
110
      mov r1,r8
111
      mov r2,r9
112
    .elif r9_dop
113
      mov r1,r9
114
      mov r2,r10
115
    .elif r10_dop
116
      mov r1,r10
117
      mov r2,r11
118
    .elif r11_dop
119
      mov r1,r11
120
      mov r2,r12
121
    .elif r12_dop
122
      mov r1,r12
123
      mov r2,r13
124
    .elif r13_dop
125
      mov r1,r13
126
      mov r2,r14
127
    .elif r14_dop
128
      mov r1,r14
129
      mov r2,r15
130
    .elif r15_dop
131
      mov r1,r15
132
      mov r2,r16
133
    .elif r16_dop
134
      mov r1,r16
135
      mov r2,r17
136
    .elif r17_dop
137
      mov r1,r17
138
      mov r2,r18
139
    .elif r18_dop
140
      mov r1,r18
141
      mov r2,r19
142
    .elif r19_dop
143
      mov r1,r19
144
      mov r2,r20
145
    .elif r20_dop
146
      mov r1,r20
147
      mov r2,r21
148
    .elif r21_dop
149
      mov r1,r21
150
      mov r2,r22
151
    .elif r22_dop
152
      mov r1,r22
153
      mov r2,r23
154
    .elif r23_dop
155
      mov r1,r23
156
      mov r2,r24
157
    .elif r24_dop
158
      mov r1,r24
159
      mov r2,r25
160
    .elif r25_dop
161
      mov r1,r25
162
      mov r2,r26
163
    .elif r26_dop || X_dop || xl_dop
164
      mov r1,r26
165
      mov r2,r27
166
    .elif r27_dop || xh_dop
167
      mov r1,r27
168
      mov r2,r28
169
    .elif r28_dop || Y_dop || yl_dop
170
      mov r1,r28
171
      mov r2,r29
172
    .elif r29_dop || yh_dop
173
      mov r1,r29
174
      mov r2,r30
175
    .elif r30_dop || Z_dop || zl_dop
176
      mov r1,r30
177
      mov r2,r31
178
    .else
179
      .error "Macro movreg16 2 reg zu gross"
180
    .endif    
181
  .elif r2_mpr
182
    setrdop
183
    .set @1_dop = 1
184
    .if r0_dop
185
      movw r3:r2,r1:r0
186
    .elif r1_dop || r3_dop
187
      .error "Macro movreg16 @0 ueber @1"
188
    .elif r2_dop
189
      movw r3:r2,r3:r2
190
    .elif r4_dop
191
      movw r3:r2,r5:r4
192
    .elif r5_dop
193
      mov r2,r5
194
      mov r3,r6
195
    .elif r6_dop
196
      movw r3:r2,r7:r6
197
    .elif r7_dop
198
      mov r2,r7
199
      mov r3,r8
200
    .elif r8_dop
201
      movw r3:r2,r9:r8
202
    .elif r9_dop
203
      mov r2,r9
204
      mov r3,r10
205
    .elif r10_dop
206
      movw r3:r2,r11:r10
207
    .elif r11_dop
208
      mov r2,r11
209
      mov r3,r12
210
    .elif r12_dop
211
      movw r3:r2,r13:r12
212
    .elif r13_dop
213
      mov r2,r13
214
      mov r3,r14
215
    .elif r14_dop
216
      movw r3:r2,r15:r14
217
    .elif r15_dop
218
      mov r2,r15
219
      mov r3,r16
220
    .elif r16_dop
221
      movw r3:r2,r17:r16
222
    .elif r17_dop
223
      mov r2,r17
224
      mov r3,r18
225
    .elif r18_dop
226
      movw r3:r2,r19:r18
227
    .elif r19_dop
228
      mov r2,r19
229
      mov r3,r20
230
    .elif r20_dop
231
      movw r3:r2,r21:r20
232
    .elif r21_dop
233
      mov r2,r21
234
      mov r3,r22
235
    .elif r22_dop
236
      movw r3:r2,r23:r22
237
    .elif r23_dop
238
      mov r2,r23
239
      mov r3,r24
240
    .elif r24_dop
241
      movw r3:r2,r25:r24
242
    .elif r25_dop
243
      mov r2,r25
244
      mov r3,r26
245
    .elif r26_dop || X_dop || xl_dop
246
      movw r3:r2,r27:r26
247
    .elif r27_dop || xh_dop
248
      mov r2,r27
249
      mov r3,r28
250
    .elif r28_dop || Y_dop || yl_dop
251
      movw r3:r2,r29:r28
252
    .elif r29_dop || yh_dop
253
      mov r2,r29
254
      mov r3,r30
255
    .elif r30_dop || Z_dop || zl_dop
256
      movw r3:r2,r31:r30
257
    .else
258
      .error "Macro movreg16 2 reg zu gross"
259
    .endif    
260
  .elif r3_mpr
261
    setrdop
262
    .set @1_dop = 1
263
    .if r0_dop
264
      mov r3,r0
265
;=== usw. ===

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

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:
1
.macro ldireg  ; @0 r0-r31, @1 Data8, 1-2c, 2-4b, wenn r0-r15, temp benutzt
2
  .if @1 > 0xff
3
    .error "ldireg die Zahl ist zu gross"
4
  .else
5
  setr16
6
  .set @0_mpr = 1
7
  .if OBEREREG
8
    ldi @0,@1
9
  .else
10
    ldi temp,@1
11
    mov @0,temp
12
  .endif
13
  .endif
14
.endm
15
16
.macro ldireg16  ; @0 r0-r30, @1 Data16, 2-4c, 4-8b, wenn r0-r15, temp benutzt
17
  .if @1 > 0xffff
18
    .error "ldireg16 die Zahl ist zu gross"
19
  .else
20
    setr_a
21
    .set @0_a = 1
22
    .if REG16
23
      setreg_16a
24
      ldireg RA0,low(@1)
25
      ldireg RB0,high(@1)
26
    .else
27
      .error "Macro mov16 reg zu gross"
28
    .endif
29
  defrestore
30
.endm
 , 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:
1
#ifndef OBEREREG
2
  #define OBEREREG r16_mpr || r17_mpr || r18_mpr || r19_mpr || r20_mpr || r21_mpr || r22_mpr || r23_mpr || r24_mpr || r25_mpr || r26_mpr || r27_mpr || r28_mpr || r29_mpr || r30_mpr || r31_mpr || X_mpr || xl_mpr || xh_mpr || Y_mpr || yl_mpr || yh_mpr || Z_mpr || zl_mpr || zh_mpr
3
#endif

: Bearbeitet durch User
von AvrAsm (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Maxim B. (max182)


Lesenswert?

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.

: Bearbeitet durch User
von Maxim B. (max182)


Angehängte Dateien:

Lesenswert?

Möglicherweise habe ich aber schon den Weg gefunden. Trotzdem wäre 
schön, wenn jemand noch bessere Variante findet:
1
.macro ldireg16  ; @0 r0-r30, @1 Data16, 2-4c, 4-8b, wenn r0-r15, temp benutzt
2
  .if @1 > 0xffff
3
    .error "ldireg16 die Zahl ist zu gross"
4
  .else
5
    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.

: Bearbeitet durch User
von AvrAsm (Gast)


Lesenswert?

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.

von Maxim B. (max182)


Lesenswert?

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)

: Bearbeitet durch User
von AvrAsm (Gast)


Lesenswert?

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.

von Maxim B. (max182)


Lesenswert?

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 :)

: Bearbeitet durch User
Beitrag #6894373 wurde von einem Moderator gelöscht.
Beitrag #6894377 wurde von einem Moderator gelöscht.
Beitrag #6894395 wurde von einem Moderator gelöscht.
Beitrag #6894398 wurde von einem Moderator gelöscht.
Beitrag #6894400 wurde von einem Moderator gelöscht.
Beitrag #6894403 wurde von einem Moderator gelöscht.
Beitrag #6894407 wurde von einem Moderator gelöscht.
Beitrag #6894410 wurde von einem Moderator gelöscht.
Beitrag #6894418 wurde von einem Moderator gelöscht.
Beitrag #6894422 wurde von einem Moderator gelöscht.
von AvrAsm (Gast)


Lesenswert?

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.

von Maxim B. (max182)


Lesenswert?

AvrAsm schrieb:
> mov16?
> nimm doch movw!

Habe ich.
So sieht das nun aus:
1
.macro mov64  ; @0,@1 r0-r25, 4-8c, 8-16b
2
3
    setr_a // Variable a für alle Reg
4
    setr_b // Variable b für alle Reg
5
    .set @0_a = 1 // Variable r@0 wird 1
6
    .set @1_b = 1 // Variable r@1 wird 1
7
    .if REG64W // wenn beide Reg geradzahlig
8
      defreg_64a // RA0-RA7 definiert
9
      defreg_64b // RB0-RB7 definiert
10
      movw RA0,RB0
11
      movw RA2,RB2
12
      movw RA4,RB4
13
      movw RA6,RB6
14
    .elif REG64WU // wenn beide Reg ungeradzahlig
15
      defreg_64a
16
      defreg_64b
17
      mov RA0,RB0
18
      movw RA1,RB1
19
      movw RA3,RB3
20
      movw RA5,RB5
21
      mov RA7,RB7
22
    .elif REG64 // wenn beide Reg sonstige Verhältnisse haben
23
      defreg_64a
24
      defreg_64b
25
      mov RA0,RB0
26
      mov RA1,RB1
27
      mov RA2,RB2
28
      mov RA3,RB3
29
      mov RA4,RB4
30
      mov RA5,RB5
31
      mov RA6,RB6
32
      mov RA7,RB7
33
    .else
34
      .error "Macro mov64 reg zu gross"
35
    .endif
36
    defrestore
37
.endm
AvrAsm schrieb:
> Tausch? Outreg? Noch nie verwendet.Kein Bedarf.
Wie schrieb mein Kollege aus Leipzig, J.S.Bach, in BWV163, Nur Jedem das 
Seine

: Bearbeitet durch User
von AvrAsm (Gast)


Lesenswert?

Maxim B. schrieb:
> Nur Jedem das Seine

Na bitteschön. Wenns Dir Spaß macht ist nichts gegen einzuwenden 😉

von Maxim B. (max182)


Lesenswert?

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.

: Bearbeitet durch User
Beitrag #6894571 wurde von einem Moderator gelöscht.
Beitrag #6894572 wurde von einem Moderator gelöscht.
Beitrag #6894588 wurde von einem Moderator gelöscht.
Beitrag #6894619 wurde von einem Moderator gelöscht.
von Hugo H. (hugo_hu)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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!

von Hugo H. (hugo_hu)


Lesenswert?

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 :-)

von Rainer V. (a_zip)


Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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

von AvrAsm (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

von AvrAsm (Gast)


Lesenswert?

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.

von Rainer V. (a_zip)


Lesenswert?

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

von Maxim B. (max182)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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:
1
;*
2
.macro IncRamAdr ; RamAdr      ;* 6Cy
3
    lds  MacReg, @0
4
    inc  MacReg
5
    sts  @0, MacReg
6
    tst  MacReg
7
.endmacro
Und gleichzeitiges Test auf Überlauf...

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

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.

: Bearbeitet durch User
von Jimi H. (jimih)


Lesenswert?

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?

von c-hater (Gast)


Lesenswert?

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!

von AvrAsm (Gast)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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

von Klaus W. (mfgkw)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von AvrAsm (Gast)


Lesenswert?

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 :)

von Peter D. (peda)


Lesenswert?

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.

von AvrAsm (Gast)


Lesenswert?

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.

von Maxim B. (max182)


Lesenswert?

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?

: Bearbeitet durch User
von AvrAsm (Gast)


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

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.

Beitrag #6898383 wurde vom Autor gelöscht.
von Maxim B. (max182)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

P.S. Problem mit Warning auch gelöst.

von Peter D. (peda)


Lesenswert?

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:
1
;-------------------------------------------------------------------------
2
;                               Using register
3
;-------------------------------------------------------------------------
4
.def    SaveSreg        = r1            ;for interrupt usage
5
.def    digit0          = r2
6
.def    digit1          = r3
7
.def    TCNT1H          = r4            ;cascade T1 to 16 bit
8
.def    cap0            = r5
9
.def    cap1            = r6
10
.def    last_t0         = r7
11
.def    last_t1l        = r8
12
.def    last_t1h        = r9
13
.def    t0              = r10           ;arithmetic register
14
.def    t1              = r11
15
.def    t2              = r12
16
.def    store           = r13           ;only used during adjust
17
18
;-------------------------------------------------------------------------
19
.def    CountDown       = r16           ;count down timer
20
.def    threshold       = r17
21
.def    i0              = r18           ;arithmetic loop counter
22
.def    a0              = r19           ;arithmetic operands
23
.def    a1              = r20
24
.def    a2              = r21
25
.def    b0              = r22
26
.def    b1              = r23
27
.def    b2              = r24
28
.def    refresh         = r25           ;display refresh rate
29
;-------------------------------------------------------------------------
Und hier die Verwendung:
1
;                               Division 24 / 24 bit
2
;-------------------------------------------------------------------------
3
;input:  a2..0: Divident
4
;        b2..0; Divisor
5
;
6
;output: a2..0: Quotient
7
;        t2..0: Remainder
8
;        T = 1: Quotient > 0
9
;
10
div24:  clt
11
        clr     t0
12
        clr     t1
13
        clr     t2
14
        ldi     i0, 24
15
_div1:  lsl     a0
16
        rol     a1
17
        rol     a2
18
        rol     t0
19
        rol     t1
20
        rol     t2
21
        cp      t0, b0
22
        cpc     t1, b1
23
        cpc     t2, b2
24
        brcs    _div2
25
        sub     t0, b0
26
        sbc     t1, b1
27
        sbc     t2, b2
28
        inc     a0
29
        set                     ;Quotient not zero
30
_div2:  dec     i0
31
        brne    _div1
32
        ret
33
;------------------------------------------------------------------------

von Maxim B. (max182)


Lesenswert?

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.
1
.macro div_24x24  ; @0,1,2 Ergebnis (@2 MSB), 
2
          ; @3,4,5 Rest (@5 MSB), 
3
          ; @6,7,8 Divident (@8 MSB), 
4
          ; @9,10,11 Divisor (@11 MSB)
5
          ; @12 Rest-Erweiterung, @13 Null-Register
6
    ; Division   24/24 -> 24 bit, Rest 24 bit (unsigned / unsigned)
7
    ; Divident 0...16777215
8
    ; Divisor  1...16777215
9
    ; Ergebnis 0...16777215
10
    ; Rest     0...16777214
11
    ; T=1 bedeutet Divisor = 0
12
    ; 590/470/6 Cycles (Divident=16777215 Divisor=1/Divident=0 Divisor=1/Divisor=0),
13
    ; 76 Bytes
14
  set
15
  mov @13,@9    ; Pruefung Divisor = 0?
16
  or @13,@10
17
  or @13,@11
18
  breq end  ; wenn Divisor = 0, Schluss mit T=1
19
  clt
20
  div_24x24__ @0,@1,@2,@3,@4,@5,@6,@7,@8,@9,@10,@11,@12,@13
21
 end:
22
.endm
1
.macro div_24x24_s  ; @0,1,2 Ergebnis (@2 MSB), 
2
          ; @3,4,5 Rest (@5 MSB), 
3
          ; @6,7,8 Divident (@8 MSB), 
4
          ; @9,10,11 Divisor (@11 MSB)
5
          ; @12 Rest-Erweiterung, @13 Null-Register
6
    ; Division   24/24 -> 24 bit, Rest 24 bit (signed / signed)
7
    ; Divident -8388608...8388607
8
    ; Divisor  -8388608...-1, 1...8388607
9
    ; Ergebnis -8388608...8388608
10
    ; Rest     -8388607...8388607
11
    ; T=1 bedeutet Divisor = 0
12
    ; 595/480/7 Cycles (Divident=8388607 Divisor=1/Divident=0 Divisor=1/Divisor=0),
13
    ; 152 Bytes
14
  set
15
  mov @13,@9    ; Pruefung Divisor = 0?
16
  or @13,@10
17
  or @13,@11
18
  brne PC+2
19
  rjmp end  ; wenn Divisor = 0, Schluss mit T=1
20
  
21
  clr @0  ; tmp-reg
22
  tst @8  ; Divident auf Minus pruefen
23
  brpl  mn1
24
  inc @0
25
  neg24 @6,@7,@8,@1  ; wenn Minus, Neg
26
 mn1:
27
   tst @11  ; Divisor auf Minus pruefen
28
  brpl mn2
29
  inc @0
30
  neg24 @9,@10,@11,@1  ; wenn Minus, Neg
31
 mn2:
32
   bst  @0,0
33
34
  div_24x24__ @0,@1,@2,@3,@4,@5,@6,@7,@8,@9,@10,@11,@12,@13
35
36
  brtc end    ; wenn T=1
37
  neg24 @0,@1,@2,@13  ; Ergebnis NEG
38
  neg24 @3,@4,@5,@13  ; Rest NEG
39
  clt
40
 end:
41
.endm
1
.macro div_24x24__  ; @0,1,2 Ergebnis (@2 MSB), 
2
          ; @3,4,5 Rest (@5 MSB), 
3
          ; @6,7,8 Divident (@8 MSB), 
4
          ; @9,10,11 Divisor (@11 MSB)
5
          ; @12 Rest-Erweiterung, @13 Null-Register
6
    ; Division   24/24 -> 24 bit, Rest 24 bit (unsigned / unsigned)
7
    ; Divident 0...16777215
8
    ; Divisor  1...16777215
9
    ; Ergebnis 0...16777215
10
    ; Rest     0...16777214
11
    ; 584/464 Cycles (Divident=16777215 Divisor=1/Divident=0 Divisor=1),
12
    ; 64 Bytes
13
  
14
  clr @3  ; Rest-Register leeren
15
  clr @4
16
  clr @5
17
  clr @0  ; Ergebnis leeren
18
  clr @1
19
  clr @2
20
  inc @0  ; 1 in Ergebnis
21
  clr @12  ; Rest-Erweiterung leeren
22
  clr @13  ; Null-Register leeren
23
 mn:
24
  lsl @6  ; Divident -> Restregister
25
  rol @7
26
  rol @8
27
  rol @3  ; Rest
28
  rol @4
29
  rol @5
30
  rol @12  ; Rest-Erweiterung
31
  cp @3,@9  ; mit Divisor vergleichen
32
  cpc @4,@10
33
  cpc @5,@11
34
  cpc @12,@13  ; Rest-Erweiterung mit Null-Register vergleichen
35
  brcs  mm  ; Wenn kleiner, Subtraktion ueberspringen
36
  sub @3,@9  ; Divisor subtrahieren
37
  sbc @4,@10
38
  sbc @5,@11
39
  sbc @12,@13  ; Null-Register aus Rest-Erweiterung subtrahieren
40
  sec      ; C=1
41
  rjmp PC+2
42
 mm:
43
  clc      ; C=0
44
  rol @0    ; Ergebnis rollen
45
  rol @1
46
  rol @2
47
  brcc mn
48
.endm
1
.macro neg24  ; @0-2 Data (@2 MSB), @3 temp, 7c,14b
2
  clr @3
3
  com @0
4
  com @1
5
  com @2
6
  adc @0,@3
7
  adc @1,@3
8
  adc @2,@3
9
.endm
Hier bleibt man nicht an bestimmte Register begrenzt.

: Bearbeitet durch User
von Uwe (Gast)


Lesenswert?

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.

von AvrAsm (Gast)


Lesenswert?

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

Beitrag #6899028 wurde vom Autor gelöscht.
von Maxim B. (max182)


Lesenswert?

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.

von Rainer V. (a_zip)


Lesenswert?

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

von AvrAsm (Gast)


Lesenswert?

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.

von Maxim B. (max182)


Lesenswert?

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

von LostInMusic (Gast)


Lesenswert?

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

von Rainer V. (a_zip)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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.

von AvrAsm (Gast)


Lesenswert?

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.

von Maxim B. (max182)


Lesenswert?

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.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.