Forum: Mikrocontroller und Digitale Elektronik Assembler - Fehler im Programm


von Simner (Gast)


Lesenswert?

Hallo,

ich arbeite derzeit an meinem ersten Assemblerprogramm. Es soll einen 
Counter, der bei 0 beginnt, hochzählen bis er max_value erreicht, in 
diesem Fall 99. Anschließend soll diese Zahl mit dem Counter verglichen 
werden, sollten sie gleich sein, dann soll in den Counter min_value, 
hier -99, geladen und wieder hochgezählt werden.

1
   asm volatile(
2
  "ldi r22, 0x55                \n\t"               
3
  "ldi r23, 0x44                \n\t"
4
  
5
  "cp %[commandToBot], r22       \n\t"            
6
  "breq label_inc                \n\t"            
7
8
  "cp %[commandToBot], r23       \n\t"        
9
  "breq label_dec                \n\t"         
10
11
  "label_dec:                    \n\t"
12
  "dec %[counter]                \n\t"   
13
  "jmp label_end                 \n\t" 
14
      
15
  "label_inc:                    \n\t" 
16
  "inc %[counter]                \n\t"              
17
  "cp %[counter], %[max_value]   \n\t"
18
  "breq label_high               \n\t"
19
  "jmp label_end                 \n\t"
20
21
  "label_high:                   \n\t"
22
  "mov %[counter], %[min_value]  \n\t"
23
  "jmp label_inc                 \n\t"
24
  
25
26
  "label_end:                    \n\t"
27
  "nop ; comment                 \n\t"   
28
29
: [counter] "+r" (counter)                                  
30
: [commandToBot] "r" (commandToBot), [min_value] "r" (min_value), [max_value] "r" (max_value)                       
31
 : "r22", "r23" );


Das Programm zählt ganz normal hoch, jedoch wird bei 99 kein wechsel 
vorgenommen. Liegt das an dem Vergleich und anschließend dem Kopieren 
der Zahl in den Counter? Oder ist es nicht ganz so einfach mit negativen 
Zahlen im Assembler zu arbeiten? Ich kann leider den Fehler nicht 
identifizieren und korrigieren. Ich hoffe, Ihr könnt mir weiterhelfen.

MfG Simner

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Ähm was für ein Schwachsinn, sorry ...

Wieso nimmst du diesen seltsamen Inline-Assembler her?

In C brauchst du keinerlei Assembler und wenn du unbedingt Assembler 
lernen möchtest, dann verwende einen Assembler und nicht diesen 
Inline-C-Assembler-Quatsch, bei dem man nicht weiß, was passiert ;-)

von Dennis (Gast)


Lesenswert?

Simner schrieb:
> Ich hoffe, Ihr könnt mir weiterhelfen.

Aber gerne doch:

Hilfe Nr. 1: poliere doch mal bitte die universelle Glaskugel oder 
alternativ teile uns doch mal mit welche Architektur es denn sein 
soll...

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Hmm, sieht für mich nach AVR aus

von Axel S. (a-za-z0-9)


Lesenswert?

Dein Code ist unsinnig. Z.B. sehe ich folgende Sequenz (das nop steht 
nur zur Illustration da, real wäre da Code, der was tut)

1
       cp foo, bar
2
       breq label
3
4
label: nop

Du vergleichst zwei Werte und willst, das bei Gleichheit zu label 
gesprungen wird. Nur kommt label direkt nach dem Sprungbefehl. Was 
glaubst du wohl, was die CPU als nächstes ausführt wenn der Sprung 
nicht genommen wird?

von Simner (Gast)


Lesenswert?

ich programmiere über Arduino auf einen Atmega 2560.
Tut mir leid, dass ich es nicht gleich erwähnt habe.

von Norbert T. (atos)


Lesenswert?

>Oder ist es nicht ganz so einfach mit negativen Zahlen im Assembler zu arbeiten?

Also wenn in einem Register z. B. 9D drinsteht (Hex) und du davon 
ausgegangen bist, dass es -99 (Dez), dann liegst du falsch - im Register 
steht 157 (Dez). Wenn du Assembler benutzt, würde ich Atmel Studio 
empfehlen, bei dem Inline-Ding blicken wenige durch... ich nicht...

Ferner... Axel erwähnte es bereits:
  "label_inc:                    \n\t"
  "inc %[counter]                \n\t"
  "cp %[counter], %[max_value]   \n\t"
  "breq label_high               \n\t"
  "jmp label_end                 \n\t"


Ich nehme an, du möchtest den counter erhöhen (mehrmals), bis dieser den 
Wert max_value erreicht, wenn also counter=max_value dann springst du zu 
label high, wenn aber nicht, dann springst du zu label_end - heißt der 
counter wird hier nur ein Mal inkrementiert.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Simner schrieb:
> ich arbeite derzeit an meinem ersten Assemblerprogramm.

Dann schreibs auch als Assemblersource (*.S) und nicht dieses völlig 
verquaste Inline.

Warum denkst Du überhaupt, daß Du besseren Assembler erzeugen kannst, 
als der Compiler?

von Besucher (Gast)


Lesenswert?

Simner schrieb:
> Das Programm zählt ganz normal hoch, jedoch wird bei 99 kein wechsel
> vorgenommen.

Naja, der Assemblercode sollte da eigentlich schon einen Wechsel 
vornehmen (hat gerade beim Testen auch so funktioniert). Da sollte der 
Fehler irgendwo in deinem restlichen Programm zu finden sein. Hast du 
darauf geachtet signed ints zu verwenden? Wie Norbert schon schrieb sind 
-99 und +157 lediglich unterschiedliche Interpretationen des gleichen 
Bitmusters, und hier muss man dem Compiler schon sagen wie man's 
interpretiert haben möchte.

Das Program hier
1
void setup() { 
2
    Serial.begin(9600); 
3
}
4
5
void loop() {
6
    int8_t commandToBot = 0x55;
7
    char counter = 0;
8
    char min_value = -99;
9
    char max_value = 99;
10
    
11
    while(true) {
12
  asm volatile(
13
    "ldi r22, 0x55                \n\t"               
14
    "ldi r23, 0x44                \n\t"
15
16
    "cp %[commandToBot], r22       \n\t"            
17
    "breq label_inc                \n\t"            
18
19
    "cp %[commandToBot], r23       \n\t"        
20
    "breq label_dec                \n\t"         
21
22
    "label_dec:                    \n\t"
23
    "dec %[counter]                \n\t"   
24
    "jmp label_end                 \n\t" 
25
      
26
    "label_inc:                    \n\t" 
27
    "inc %[counter]                \n\t"              
28
    "cp %[counter], %[max_value]   \n\t"
29
    "breq label_high               \n\t"
30
    "jmp label_end                 \n\t"
31
    "jmp label_end                 \n\t"
32
33
    "label_high:                   \n\t"
34
    "mov %[counter], %[min_value]  \n\t"
35
    "jmp label_inc                 \n\t"
36
37
38
    "label_end:                    \n\t"
39
    "nop ; comment                 \n\t"   
40
41
    : [counter] "+r" (counter)
42
    : [commandToBot] "r" (commandToBot), [min_value] "r" (min_value), [max_value] "r" (max_value)                       
43
    : "r22", "r23" 
44
  );
45
46
        Serial.print((int8_t)counter);
47
        Serial.print("\t");
48
        Serial.print((uint8_t)counter);
49
        Serial.print("\n");
50
    } 
51
}

erzeugt bei mir die Ausgabe:
1
1  1
2
2  2
3
3  3
4
 ...usw...
5
97  97
6
98  98
7
-98  158
8
-97  159
9
-96  160
10
 ...usw...
11
-2  254
12
-1  255
13
0  0
14
1  1
15
2  2
16
 ...usw...
Der Wechsel ist deutlich zu sehen, und bei der Betrachtung von Counter 
als signed int ist das Ergebnis auch wie gewünscht (vorausgesetzt 
natürlich das wirklich das Intervall ]-99, 99[ gewünscht ist, und 
nicht[-99, 99]).

von Simner (Gast)


Lesenswert?

Danke schon mal für die Tipps und Kommentare.

Im Prinzip sieht mein Programm ähnlich aus. Ich nutze aber für counter, 
min_value und max_value signed ints.

Das mit dem unterschiedlichen Interpretationen war mir nicht klar. Ich 
lasse mir den Counter über ein 7-Segment-Display anzeigen. Der Wechsel 
erfolgt dann doch, da er von 99 auf 157 springt und weiter hochzählt.

Wie kann ich dem Compiler sagen, dass ich die 157 als -99 interpretiert 
haben möchte?

von Besucher (Gast)


Lesenswert?

Das wird dann wohl nicht mehr am Compiler sondern an den Funktionen zur 
Ansteuerung des Displays liegen. Sind dort denn negative Zahlen 
vorgesehen? Falls nicht dann muss man sich das halt selber dazustricken 
(ohne die Funktionen zu kennen kann man dazu allerdings kaum hilfreiche 
Tipps geben..)

von c-hater (Gast)


Lesenswert?

Peter D. schrieb:

> Simner schrieb:
>> ich arbeite derzeit an meinem ersten Assemblerprogramm.
>
> Dann schreibs auch als Assemblersource (*.S) und nicht dieses völlig
> verquaste Inline.
>
> Warum denkst Du überhaupt, daß Du besseren Assembler erzeugen kannst,
> als der Compiler?

Das hat er nicht behauptet. Ganz im Gegenteil, er wies ausdrücklich 
darauf hin, dass es sein erstes Assemblerprogramm ist, und du wußtest 
das, denn du hast diese Aussage sogar selber zitiert.

Klar: jemand, der gerade sein erstes Assemblerprogramm schreibt, ist 
höchstens zufällig bei einer kleinen Teilroutine mal nennenswert besser 
als ein heutiger C-Compiler. In aller Regel wird er bestenfalls gleich 
gut oder gar schlechter sein.

Aber: jeder fängt mal an. Man muss eben aus seinen Fehlern einfach mal 
nur lernen. Es fängt an, cool zu werden, wenn man in der Lage ist, die 
Sache wenigstens erstmal korrekt in Asm umzusetzen.

Soweit ist der TO allerdings offensichtlich noch lange nicht. Zwischen 
dem geposteten Programm und der verbalen Beschreibung der gewünschten 
Funktion besteht doch eine ganz erhebliche Diskrepanz...

Ich vermute allerdings ganz stark: der TO ist auch nicht in der Lage, 
die gewünschte Funktionalität korrekt in C umzusetzen...

Ich sage nur: Arduino. Das sagt eigentlich schon alles...

von Peter D. (peda)


Lesenswert?

c-hater schrieb:
> Zwischen
> dem geposteten Programm und der verbalen Beschreibung der gewünschten
> Funktion besteht doch eine ganz erhebliche Diskrepanz...

Das wundert mich garnicht, wenn man das falsche Werkzeug benutzt.
Inline Assembler fasse ich nichtmal mit der Kneifzange an. Ich muß mir 
meine Gehirnwindungen nicht unnütz verknoten.
Dessen Berechtigung sehe ich ausschließlich in *.h-Files (delay.h usw.).

Ich hab nichts gegen Assembler lernen. Dann aber im richtigen Assembler 
und mit aussagefähigen Registernamen und Kommentaren. Sonst lernt man 
nichts außer irgendwo was abzuschreiben.

Aber auch in Assembler gilt, alle verwendeten Symbole einem Leser 
bekannt zu machen. Er kann schließlich nicht in Deinen Kopf sehen.
Und diese magischen 0x55, 0x44 sollte man auch erklären.

: Bearbeitet durch User
von Mampf F. (mampf) Benutzerseite


Lesenswert?

c-hater schrieb:
> Aber: jeder fängt mal an.

Ja genau! Und ich zähle die Verwendung des Inline-Assemblers zu 
"Anfängerfehler" :)

von Norbert T. (atos)


Lesenswert?

> Wie kann ich dem Compiler sagen, dass ich die 157 als -99 interpretiert
> haben möchte?

Gar nicht. Wie Besucher sagte, du musst die entsprechende 
Display-Routine anpassen. Wenn man Assembler lernen möchte, sollte man 
immer das "Instruction Set Manual" anschauen, um zu sehen welche 
Kommandos überhaupt mit negativen Zahlen arbeiten. Dann sollte man 
wissen, dass in einem Register stets nur eine Zahl zwischen 0 und 255 
stehen wird. Dies hier:

LDI r16,0x00
DEC r16

wird ins Register r16 die Zahl 0 laden. Dann wird die 0 dekrementiert, 
was zu folge hat, dass dann in r16 die Zahl 255 steht. Wenn man 
allerdings im "Instruction Set Manual" nach dem Kommando DEC nachschaut, 
wird man feststellen können, dass DEC u. a. das Negativ-Flag beeinflußt. 
(Einige Kommandos arbeiten halt mit signed andere mit unsigned.) Wenn 
man also das N Flag im obigen Beispiel nach der Ausführung von DEC 
überprüft, wird man feststellen, dass es gesetzt wurde, was nun heißt, 
dass man eine negative Zahl im Register hat. -1 in Hex ist ff, also 255. 
Dies heißt deine Anzeige-Routine muss nach DEC oder INC das N-Flag 
überprüfen um dann die Zahl entsprechend als -1 oder 255 anzuzeigen.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Norbert T. schrieb:
> Dies heißt deine Anzeige-Routine muss nach DEC oder INC das N-Flag
> überprüfen um dann die Zahl entsprechend als -1 oder 255 anzuzeigen.

Nein.

Ob 0xFF in einem 8-Bit Register 255 oder -1 bedeutet, ist allein eine 
Interpretationssache. In C gibts dafür die signed und unsigned 
Modifier an den Integer-Typen. In Assembler muß der Programmierer das 
selber wissen. Genauso wie er seine Variablen auf Register (und RAM) 
verteilen muß, muß er auch wissen ob sie vorzeichenbehaftet sind.

Das N-Flag wird von vielen arithmetischen Operationen beeinflußt. Die 
ALU setzt das Flag immer so, wie es für vorzeichenbehaftete Zahlen 
gesetzt sein muß. Ob das Programm das Flag auswertet, hängt einzig davon 
ab, was die Zahlen in den Registern bedeuten. Wenn sie unsigned sind, 
dann hat das N-Flag schlicht keine Bedeutung. Und manchmal sind die 
Registerinhalte auch gar keine Zahlen, sondern z.B. Zeichencodes.

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