Hallo zusammen,
sorry für den Threadtitel, mir ist nix anderes eingefallen...
Ich habe hier eine Tabelle und möchte daraus Datendefinitionen erzeugen.
1
.equ A0=2
2
.equ A1=5
3
.equ A2=7
4
.equ A3=4
5
.equ A4=1
6
.equ A5=0
7
.equ A6=3
8
.equ A7=8
9
.equ A8=9
10
.equ A9=6
Dazu habe ich zunächst folgendes Macro definiert:
1
#define b(x) A##x
2
.macro c
3
.db b(@0),@1
4
.endmacro
Solange ich das statisch verwende, klappt es wie vorgesehen.
1
c 0,0
2
c 1,0
3
c 2,0
4
5
Listfile:
6
000000 0002 c 0,0
7
000001 0005 c 1,0
8
000002 0007 c 2,0
Man sieht, es wurden die Definitionen aus den Tabellenplätzen 0 bis 2
(2,5 und 7) eingesetzt.
Nun hätte ich das aber gern flexibel, d.h. relativ zu einer
Tabellenstartposition.
So funktioniert's schonmal nicht:
1
.set d=0
2
c d,0
3
c d+1,0
4
c d+2,0
5
6
Errors:
7
Tabtest.asm(27): error: Undefined symbol: Ad
8
Tabtest.asm(35): info: macro 'c' called here
9
Tabtest.asm(27): error: Undefined symbol: Ad
10
Tabtest.asm(36): info: macro 'c' called here
11
Tabtest.asm(27): error: Undefined symbol: Ad
12
Tabtest.asm(37): info: macro 'c' called here
Aber selbst so geht's nicht wie benötigt:
1
c 0,0
2
c 0+1,0
3
c 0+2,0
4
5
Listfile:
6
000000 0002 c 0,0
7
000001 0003 c 0+1,0
8
000002 0004 c 0+2,0
Es wird stets die Definition aus Tabellenplatz 0 verwendet und danach
noch der Offset dazuaddiert.
OK, also habe ich's auch mal so probiert:
1
#define A0 2
2
#define A1 5
3
#define A2 7
4
#define A3 4
5
#define A4 1
6
#define A5 0
7
#define A6 3
8
#define A7 8
9
#define A8 9
10
#define A9 6
11
12
#define b(x) A##x
13
.macro c
14
.db b(@0),@1
15
.endmacro
16
17
c 0,0
18
c 1,0
19
c 2,0
20
21
Errors:
22
Tabtest.asm(27): error: Invalid symbol in this context: A0(11)
23
Tabtest.asm(30): info: macro 'c' called here
24
Tabtest.asm(27): error: Invalid symbol in this context: A1(11)
25
Tabtest.asm(31): info: macro 'c' called here
26
Tabtest.asm(27): error: Invalid symbol in this context: A2(11)
27
Tabtest.asm(32): info: macro 'c' called here
Hier geht schon die statische Definition in die Hose.
Ziel soll sein, mit dem Macro die Daten der Tabelle in gewisser Weise
dynamisch zu verwenden, z.B.:
1
.set d=0
2
c d,0
3
c d+1,0
4
c d+2,0
5
.set d=3
6
c d,1
7
c d+1,1
8
c d+2,1
9
10
Oder noch besser:
11
12
.macro e
13
.set d=@0
14
c d,@1
15
c d+1,@1
16
c d+2,@1
17
.endmacro
18
19
e 0,0
20
e 3,1
Kann ich das Ziel überhaupt mit den Mitteln des AVRASM 2.1.42 erreichen?
Horst M. schrieb:> Ich habe hier eine Tabelle und möchte daraus Datendefinitionen erzeugen.
Also ich habe mir das Zeug wirklich mehrmals aufmerksam durchgelesen,
aber mir ist immer noch nicht ganz klar, was du eigentlich genau
erreichen willst. Immer wenn ich glaubte, verstanden zu haben, was du
beabsichtigst, war da wieder etwas, was dagegen sprach.
Vermutlich versuchst du dein eigentliches Problem von hinten durch die
Brust in's Auge zu lösen. Oder anders ausgedrückt: vermutlich falscher
Ansatz.
Aber unabhängig davon hast du wohl Probleme mit dem Verständnis dieses
Designtime-Gelumpes. Dazu muss man mehrere Sachen wissen.
Das Zeug ist längst nicht so einfach zu beherrschen wie etwa der
C-Präprozessor (und da allein lauern ja schon genug Fallstricke).
Immerhin kannst du aber, solange du dich auf Macros im C-Stil
beschränkst (und dazu gehört z.B. weder .equ noch .set oder .macro),
darauf verlassen, dass sich der Kram auch tatsächlich wie der
C-Präprozessor verhält. Sprich: reine Textersetzung ist und den
entsprechenden Gesetzen folgt.
Deutlich schwieriger zu verstehen ist der Assembler-Makrokram. Dabei
handelt sich NICHT nur um reine Textersetzung, hier wird u.U.
tatsächlich schon zur Designzeit gerechnet. Das macht diese
Macro-Sprache sehr viel mächtiger als einen C-Präprozessor, aber leider
auch nochmal sehr viel fehlerträchtiger. Das scheinbare Chaos entsteht
durch zwei Sachen. Erstens ist es nicht immer ganz leicht zu erkennen,
wann genau der Interpreter zur Evaluation greift, also den Wert von
Ausdrücken schon während des Übersetzens ermittelt. Und zweitens handelt
es sich um einen Zweipass-Assembler, der ganze Kram wird also zweimal
interpretiert und zusammen mit erstens sorgt das dafür, dass
unterschiedliche Ergebnisse im ersten und zweiten Lauf herauskommen
können. Was in aller Regel Müll ergibt, weil es keinerlei Sprachmittel
gibt, um im ersten Lauf gezielt etwas andere zu tun als im zweiten.
Und richtig spannend und bezüglich der herrschenden Gesetzmäßigkeiten
kaum noch überschaubar ist es, wenn du die beiden Sachen auch noch
mischst. Was wohl auch der Grund ist, dass der Kram nirgendwo
systematisch logisch konsistent dokumentiert ist. Hier haben ganz
offensichtlich die Entwickler selber ihr Werk schon nicht mehr
überblickt...
> Kann ich das Ziel überhaupt mit den Mitteln des AVRASM 2.1.42 erreichen?
Tja, wenn ich verstanden hätte, was dein eigentliches Ziel ist, hätte
ich dir die Frage vielleicht sogar beantworten können...
Also gut, ich fange nochmal an.
Gehen wir von zehn Instanzen aus, die in Gruppen gesteuert werden
sollen.
Das ist natürlich nur ein Beispiel, real geht's um ein, zwei Dutzend mal
soviel.
Solange die Instanzen z.B. stetig aufsteigend adressiert werden können,
ist das simpel.
1
--+
2
1|-> Eins
3
2|-> Zwei
4
3|-> Drei
5
4|-> Vier
6
5|-> Fünf
7
6|-> Sechs
8
7|-> Sieben
9
8|-> Acht
10
9|-> Neun
11
10|-> Zehn
12
--+
Es wird ein Macro definiert, mit dem für 5 aufeinanderfolgende Instanzen
ein Parameter definiert wird.
1
.macro group5
2
.db @0,@1
3
.db @0+1,@1
4
.db @0+2,@1
5
.db @0+3,@1
6
.db @0+4,@1
7
.endmacro
Damit lassen sich leicht verschiedene Definitionen erzeugen.
1
variant1:
2
group5 1,0 ;weise den Instanzen 1...5 den Parameter 0 zu
3
group5 6,1 ;weise den Instanzen 6...10 den Parameter 1 zu
4
5
variant2:
6
group5 1,3 ;weise den Instanzen 1...5 den Parameter 3 zu
7
group5 6,2 ;weise den Instanzen 6...10 den Parameter 2 zu
8
9
variant3:
10
group5 3,4 ;weise den Instanzen 3...7 den Parameter 4 zu
11
12
Listfile:
13
variant1:
14
000000 0001
15
000001 0002
16
000002 0003
17
000003 0004
18
000004 0005 group5 1,0
19
000005 0106
20
000006 0107
21
000007 0108
22
000008 0109
23
000009 010a group5 6,1
24
25
variant2:
26
00000a 0301
27
00000b 0302
28
00000c 0303
29
00000d 0304
30
00000e 0305 group5 1,3
31
00000f 0206
32
000010 0207
33
000011 0208
34
000012 0209
35
000013 020a group5 6,2
36
37
variant3:
38
000014 0403
39
000015 0404
40
000016 0405
41
000017 0406
42
000018 0407 group5 3,4
Ich frage nun nach einer Möglichkeit, die Instanzen weiterhin in der
gezeigten Form zu steuern (also gruppenweise aufsteigend), obwohl sie
NICHT mehr stetig addressiert werden können.
Die Zuordnung der Adresse zur Instanz soll beim Assemblieren definiert
werden, nicht zur Laufzeit.
1
--+
2
3|-> Eins
3
6|-> Zwei
4
8|-> Drei
5
5|-> Vier
6
2|-> Fünf
7
1|-> Sechs
8
4|-> Sieben
9
9|-> Acht
10
10|-> Neun
11
7|-> Zehn
12
--+
Die Adressen der Instanzen werden in einer Art Tabelle definiert.
1
.equ A1=3
2
.equ A2=6
3
.equ A3=8
4
.equ A4=5
5
.equ A5=2
6
.equ A6=1
7
.equ A7=4
8
.equ A8=9
9
.equ A9=10
10
.equ A10=7
Für die Umsetzung der "logischen" in die "physische" Adresse der Instanz
wird das folgende Define benutzt.
1
#define b(x) A##x
Damit kann zumindest bei statischen Definitionen mit der logischen
Adresse gearbeitet werden.
1
.macro c
2
.db b(@0),@1
3
.endmacro
4
5
variant1:
6
c 1,0
7
c 2,0
8
c 3,0
9
c 4,0
10
c 5,0
11
c 6,1
12
c 7,1
13
c 8,1
14
c 9,1
15
c 10,1
16
17
variant2:
18
c 1,3
19
c 2,3
20
c 3,3
21
c 4,3
22
c 5,3
23
c 6,2
24
c 7,2
25
c 8,2
26
c 9,2
27
c 10,2
28
29
variant3:
30
c 3,4
31
c 4,4
32
c 5,4
33
c 6,4
34
c 7,4
35
36
Listfile:
37
variant1:
38
000000 0003 c 1,0
39
000001 0006 c 2,0
40
000002 0008 c 3,0
41
000003 0005 c 4,0
42
000004 0002 c 5,0
43
000005 0101 c 6,1
44
000006 0104 c 7,1
45
000007 0109 c 8,1
46
000008 010a c 9,1
47
000009 0107 c 10,1
48
49
variant2:
50
00000a 0303 c 1,3
51
00000b 0306 c 2,3
52
00000c 0308 c 3,3
53
00000d 0305 c 4,3
54
00000e 0302 c 5,3
55
00000f 0201 c 6,2
56
000010 0204 c 7,2
57
000011 0209 c 8,2
58
000012 020a c 9,2
59
000013 0207 c 10,2
60
61
variant3:
62
000014 0408 c 3,4
63
000015 0405 c 4,4
64
000016 0402 c 5,4
65
000017 0401 c 6,4
66
000018 0404 c 7,4
Das Ziel ist aber, wie im Beispiel ganz oben gruppenweise zu arbeiten.
1
.macro group5
2
.db b(@0),@1
3
.db b(@0+1),@1
4
.db b(@0+2),@1
5
.db b(@0+3),@1
6
.db b(@0+4),@1
7
.endmacro
8
9
variant1:
10
group5 1,0
11
group5 6,1
12
13
variant2:
14
group5 1,3
15
group5 6,2
16
17
variant3:
18
group5 3,4
19
20
Listfile:
21
variant1:
22
000000 0003
23
000001 0004
24
000002 0005
25
000003 0006
26
000004 0007 group5 1,0
27
000005 0101
28
000006 0102
29
000007 0103
30
000008 0104
31
000009 0105 group5 6,1
32
33
variant2:
34
00000a 0303
35
00000b 0304
36
00000c 0305
37
00000d 0306
38
00000e 0307 group5 1,3
39
00000f 0201
40
000010 0202
41
000011 0203
42
000012 0204
43
000013 0205 group5 6,2
44
45
variant3:
46
000014 0408
47
000015 0409
48
000016 040a
49
000017 040b
50
000018 040c group5 3,4
So funktioniert's schonmal nicht (und es ist auch nicht wirklich
überraschend).
Es wird immer die beim Aufruf des group5-Macros angegebene logische
Adresse in die physische Adresse umgewandelt und danach der im Macro
jeweils angegebene Offset addiert.
Zum Beispiel:
Ich habe noch ein paar andere Konstrukte getestet.
Etwa:
1
.macro group5
2
.db A@0,@1
3
.db A@0+1,@1
4
.db A@0+2,@1
5
.db A@0+3,@1
6
.db A@0+4,@1
7
.endmacro
Geht also auch ohne Define usw. (das "A" und der Macro-Parameter @0
werden verknüpft), ergibt aber dasselbe wie oben.
1
.macro b
2
.db A@0,@1
3
.endmacro
4
5
.macro group5
6
.set c=@0
7
b c,@1
8
.set c=c+1
9
b c,@1
10
.set c=c+1
11
b c,@1
12
.set c=c+1
13
b c,@1
14
.set c=c+1
15
b c,@1
16
.endmacro
17
18
Ergibt Fehler wie:
19
Tabtest.asm(29): error: Undefined symbol: Ac
20
Tabtest.asm(34): info: macro 'b' called here
21
Tabtest.asm(46): info: macro 'group5' called here
22
Tabtest.asm(29): error: Undefined symbol: Ac
23
Tabtest.asm(36): info: macro 'b' called here
24
Tabtest.asm(46): info: macro 'group5' called here
"c" wird hier offensichtlich literal verwendet und nicht als Variable.
Die grundsätzliche Frage ist m.E., ob es überhaupt einen Weg gibt,
zunächst einen Ausdruck wie "@0+1" oder "c+1" numerisch zu berechnen und
dann mit dem Ergebnis ein neues Symbol zu basteln, das vom Assembler
entsprechend verarbeitet werden kann.
ist auch nicht wirklich schön, aber dafür kurz. Du müsstest vom
Assembler aus auf die Symbole variant1-variant3 problemlos zugreifen
können, wenn du den Code einfach in eine .cpp -Datei packst und
mitkompilierst.
Horst M. schrieb:> Die grundsätzliche Frage ist m.E., ob es überhaupt einen Weg gibt,> zunächst einen Ausdruck wie "@0+1" oder "c+1" numerisch zu berechnen und> dann mit dem Ergebnis ein neues Symbol zu basteln, das vom Assembler> entsprechend verarbeitet werden kann.
Wenn das das grundsätzliche Problem sein soll (ich bin nicht sicher, ob
es das ist): Ja, das geht.
Du musst halt nur den Interpreter dazu zwingen den Ausdruck zu
evaluieren. Das ist leicht. Du musst ihn nur in einer .set-Direktive
verwenden und dann das mittels .set zugewiesene Symbol in der Bedingung
einer .if-Direktive verwenden. Damit zwingst du den Interpreter zur
Evaluierung des Symbols.
Ab diesem Moment steht das in .set zugewiesene Symbol nicht mehr als
reine Textersetzung zur Verfügung, sondern als fertig berechneter Wert.
Wenn du jetzt noch dafür sorgst, dass im zweiten Assemblerlauf auch
wieder genau derselbe Wert herauskommt, sollte nichts mehr schief gehen.
Das ist eigentlich ziemlich leicht, solange man nicht mit Adressen des
erzeugten Codes hantiert...
Mittlerweile hab ich's hinbekommen, allerdings nicht mit irgendwelchen
String-Substituierungen oder der Zusammensetzung von neuen Symbolen.
Ich verwende ein Monster-Macro (OK, im Beispiel noch nicht ganz so
monströs, aber die eigentliche Anwendung hat wie erwähnt einen deutlich
größeren Wertebereich), um jeden Eingangswert in den entsprechenden
Ausgangswert umzusetzen.
Das ist weder cool noch effizient und skaliert auch nicht wirklich, aber
es tut genau das, was getan werden soll.
1
.equ A1=3
2
.equ A2=6
3
.equ A3=8
4
.equ A4=5
5
.equ A5=2
6
.equ A6=1
7
.equ A7=4
8
.equ A8=9
9
.equ A9=10
10
.equ A10=7
11
.macro a
12
.if @0==1
13
.set _x=A1
14
.elif @0==2
15
.set _x=A2
16
.elif @0==3
17
.set _x=A3
18
.elif @0==4
19
.set _x=A4
20
.elif @0==5
21
.set _x=A5
22
.elif @0==6
23
.set _x=A6
24
.elif @0==7
25
.set _x=A7
26
.elif @0==8
27
.set _x=A8
28
.elif @0==9
29
.set _x=A9
30
.elif @0==10
31
.set _x=A10
32
.else
33
.error "Out of range"
34
.endif
35
.endmacro
36
37
.macro b
38
a @0
39
.db _x,@1
40
.endmacro
41
42
.macro group5
43
b @0,@1
44
b @0+1,@1
45
b @0+2,@1
46
b @0+3,@1
47
b @0+4,@1
48
.endmacro
49
50
variant1:
51
group5 1,0
52
group5 6,1
53
54
variant2:
55
group5 1,3
56
group5 6,2
57
58
variant3:
59
group5 3,4
60
61
Listfile:
62
variant1:
63
000000 0003
64
000001 0006
65
000002 0008
66
000003 0005
67
000004 0002 group5 1,0
68
000005 0101
69
000006 0104
70
000007 0109
71
000008 010a
72
000009 0107 group5 6,1
73
74
variant2:
75
00000a 0303
76
00000b 0306
77
00000c 0308
78
00000d 0305
79
00000e 0302 group5 1,3
80
00000f 0201
81
000010 0204
82
000011 0209
83
000012 020a
84
000013 0207 group5 6,2
85
86
variant3:
87
000014 0408
88
000015 0405
89
000016 0402
90
000017 0401
91
000018 0404 group5 3,4
Ist ein bißchen retro, weil man damit wieder auf den Assembler warten
muß.
Die Erzeugung von 25000 Bytes mit dem obigen Beispiel läuft auf einem
3 GHz-PC ca. 10 Sekunden.
Bei der eigentlichen Anwendung habe ich für ca. 45 KByte Code lockere
3 Minuten warten müssen...
Ob die Ausgangswerte vorher mit .equ definiert oder im Macro direkt
zugewiesen werden, hat auf die Laufzeit keinen Einfluß.
Ich habe auch mal getestet, ob es mit einem binären Verfahren - sowas
wie
1
.if @0<8
2
.if @0<4
3
.if @0<2
4
.if @0==1
5
.set _x=A1
6
.endif
7
.elif @0==2
8
.set _x=A2
9
.else
10
.set _x=A3
11
.endif
12
.elif @0<6
13
.if @0==4
14
.set _x=A4
15
.else
16
.set _x=A5
17
.endif
18
.elif @0==6
19
.set _x=A6
20
.else
21
.set _x=A7
22
.endif
23
.else
24
...
beschleunigt werden kann - das Gegenteil war der Fall.
Der Assembler kommt offensichtlich mit dem Straight-Forward-Ansatz am
besten zurecht.
Ein bißchen schneller läuft's, wenn das Macro zur Umsetzung nochmal
unterteilt wird.
1
.macro a_1
2
.if @0==1
3
.set _x=A1
4
.elif @0==2
5
.set _x=A2
6
.elif @0==3
7
.set _x=A3
8
.elif @0==4
9
.set _x=A4
10
.else
11
.set _x=A5
12
.endif
13
.endmacro
14
15
.macro a_2
16
.if @0==6
17
.set _x=A6
18
.elif @0==7
19
.set _x=A7
20
.elif @0==8
21
.set _x=A8
22
.elif @0==9
23
.set _x=A9
24
.else
25
.set _x=A10
26
.endif
27
.endmacro
28
29
.macro b
30
.if @0<1 || @0>10
31
.error "Out of range."
32
.endif
33
.if @0<6
34
a_1 @0
35
.else
36
a_2 @0
37
.endif
38
.db _x,@1
39
.endmacro
Mit diesem Prinzip läuft die Erzeugung von 45 KByte Code mit dem
Originalquelltext auf meinem Rechner knapp eine Minute.
Immer noch recht zäh, aber ist halt so...
Ungefähr an diesem Punkt müssen die Computerianer gewesen sein, als sie
im letzten Jahrtausend den Entschluß gefasst haben, höhere
Programmiersprachen zu erfinden.
Oliver