Bei der C-Compilierung (Assemblierung) des untenstehenden Code-Schnipsels erhalte ich die Fehlermeldung: ...\Temp/ccC9yBGs.s:37: Error: constant value required Gibt es im Forum einen Experten für diesen speziellen Fall, der mir helfen kann? Ich habe vieles versucht, aber mir fällt nichts Gescheites mehr ein. Wo liegt der Fehler? Danke im voraus Georg static void lcd_out( uint8_t data ) { data &= 0xF0; // Die 4 Bits im oberen Nibble spiegeln // MSB LSB // bit7 bit6 bit5 bit4 0 0 0 0 // Nach der Spiegelung: // MSB LSB // bit4 bit5 bit6 bit7 0 0 0 0 asm volatile ( "lds r3, data" "\n\t" "bst r3, 7" "\n\t" "bld r4, 4" "\n\t" "bst r3, 6" "\n\t" "bld r4, 5" "\n\t" "bst r3, 5" "\n\t" "bld r4, 6" "\n\t" "bst r3, 4" "\n\t" "bld r4, 7" "\n\t" "lds data, r4" "\n\t" : /* No Output List */ : /* No Input List */ : "r3", "r4" ); . . . }
Schreibe -save-temps, um die erzeugte Assembler-Datei (*.s) zu erhalten. Neben dem Fehler, den Ernst schaon aufzeigte, ist er asm-Schnippel inkorrekt: data ist eine lokale Variable, für die kein Symbol angelegt wird. Das Symbol zu referenzieren führt also zu einem Linkerfehler oder zu falschen Code, falls ein gleichnamiges Symbol existiert. Korrekt lautet die Sequenz etwa:
1 | static void lcd_out (uint8_t data) |
2 | {
|
3 | // Die 4 Bits im oberen Nibble spiegeln
|
4 | // MSB LSB
|
5 | // bit7 bit6 bit5 bit4 0 0 0 0
|
6 | // Nach der Spiegelung:
|
7 | // MSB LSB
|
8 | // bit4 bit5 bit6 bit7 0 0 0 0
|
9 | asm ("bst %1, 7" "\n\t" |
10 | "bld %0, 4" "\n\t" |
11 | "bst %1, 6" "\n\t" |
12 | "bld %0, 5" "\n\t" |
13 | "bst %1, 5" "\n\t" |
14 | "bld %0, 6" "\n\t" |
15 | "bst %1, 4" "\n\t" |
16 | "bld %0, 7" "\n\t" |
17 | : "=&r" (data) |
18 | : "r" (data)); |
19 | |
20 | data &= 0xF0; |
21 | ...
|
22 | }
|
Herzlichen Dank an Johann L. für die sehr konstruktive und prompte Antwort. Zumindest Compilierung und Assemblierung sind jetzt O.K. Eine Verständnisfrage bleibt aber noch: 1. Woher weiss die Assemblerroutine, welches Byte sie manipulieren soll und 2. wohin sie es nach getaner Arbeit abspeichern muss? Die asm-Routine - wenn ich das richtig verstanden habe - arbeitet ja nur mit lokalen Variablen. Ich muss aber doch eine aus Sicht des Assemblers externe Variable (meine Variable data ) übergeben können und auch außerhalb der Routine wieder weiterverarbeiten können.
Georg Schl. schrieb: > 1. Woher weiss die Assemblerroutine, welches Byte sie manipulieren soll Es gibt keine "Assemblerroutine" sondern nur einen String, in dem GCC Ersetzungen macht (Inline Assembler). Hier wird %0 dem ersten Operanden zugeordnet etc. ZB http://www.rn-wissen.de/index.php/Inline-Assembler_in_avr-gcc > 2. wohin sie es nach getaner Arbeit abspeichern muss? Ditto. Das wird durch das Inline-Assembler Interface beschrieben. Der Compiler allokiert die Register; das ist idR einer handverlesenen Vergabe vorzuziehen. > Die asm-Routine - wenn ich das richtig verstanden habe - arbeitet ja nur > mit lokalen Variablen. Ich muss aber doch eine aus Sicht des Assemblers > externe Variable (meine Variable data) übergeben können und auch > außerhalb der Routine wieder weiterverarbeiten können. Wie gesagt: es gibt keine Assembler-Routine. Und data ist nicht extern, zumindest nicht so, wie extern in dem Zusammenhang verstanden wird: es ist eine lokale Variable für den Compiler, und selbst wenn er sie auf dem Stack zwischenspeichern muss oder sie auf dem Stapel übergeben bekommt, gibt's kein Symbol data. Der Assembler sieht nur ein Register. Übrigens hat das rein garnix mit AVR-Studio zu tun, das ist allein ein Ding von avr-gcc bzw. gcc.
Die asm-sequenz kann funktionsidentisch durch die folgenden 2 C-Coding Zeilen ersetzt werden:
1 | data = ((data >> 1) & 0x55) | ((data << 1) & 0xaa); |
2 | data = ((data >> 2) & 0x33) | ((data << 2) & 0xcc); |
Vielleicht etwas langsamer, dafür aber nachvollziehbar und transparent.
@Georg Schl. 'funktionsähnlich'! Es soll ja nur mit den oberen 4Bits gewürfelt werden.
@ Ralf Die oberen 4 Bits sollten gespiegelt werden, und genau das leisten sowohl die beiden vorgeschlagenen C-Coding Zeilen als auch der asm-Schnipsel . Damit ist der asm-Schnipsel mit den beiden C-Coding Zeilen 'funktionsidentisch'! 'Funktionsähnlich' wäre zu wenig, denn dann müsstest Du sagen, in was sich die beiden Ergebnisse unterscheiden.
Weil wir gerade bei verschiedenen Möglichkeiten sind... Den Code in eine extra Datei -> dann geht auch der Simulator drüber, falls es mal komplizierter wird.
1 | extern void dataFunc(volatile uint8_t* data); |
2 | // 'extern' ist nicht unbedingt nötig.
|
1 | r18 = 18 |
2 | r19 = 19 |
3 | r30 = 30 |
4 | r31 = 31 |
5 | |
6 | .global dataFunc |
7 | .func dataFunc |
8 | dataFunc: |
9 | ; Übergabe von Zeiger auf 'data' erfolgt in r24:r25 |
10 | movw r30, r24 |
11 | ld r18, Z |
12 | mov r19, r18 |
13 | bst r18, 7 |
14 | bld r19, 4 |
15 | bst r18, 6 |
16 | bld r19, 5 |
17 | bst r18, 5 |
18 | bld r19, 6 |
19 | bst r18, 4 |
20 | bld r19, 7 |
21 | st Z, r19 |
22 | ret |
23 | .endfunc |
24 | .end |
@Ralf ... das gesamte untere Nibble ist aber von vornherein Null. Denn vor dem asm-Schnipsel steht
1 | data &= 0xF0; |
Aber bezogen auf den Allgemeinfall hast Du wohl recht.
Georg Schl. schrieb: > ... das gesamte untere Nibble ist aber von vornherein Null. Mmh. Hatte nicht mehr so weit hochgescrollt.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.