Hallo zusammen,
AVR Studio treibt mich noch zur Verzweiflung...
Ich habe das folgende Stück Code (Minimalbeispiel):
1
tst r0
2
brne bad
3
rjmp 1
4
bad: jmp 0
5
nop
Wenn ich das ganze auf dem ATmega flashe und dann im Disassembler
angucke, bzw. durchsteppe finde ich das folgende:
1
1: tst r0
2
+00000000: 2000 TST R0 Test for Zero or Minus
3
2: brne bad
4
+00000001: F409 BRNE PC+0x02 Branch if not equal
5
3: rjmp 1
6
+00000002: CFFE RJMP PC-0x0001 Relative jump
7
@00000003: bad
8
4: bad: jmp 0
9
+00000003: 940C0000 JMP 0x00000000 Jump
10
5: nop
11
+00000005: 0000 NOP No operation
Kann mir jemand erklären, warum aus dem rjmp 1 ein RJMP PC-0x0001 wird
??
Ich hab das ganze jetzt durch ein zweites Label und ein BREQ zu dem
Label anstelle von dem rjmp gelöst, aber ich würde trotzdem gerne den
Fehler an meiner Überlegung hierüber verstehen...
Gruß und besten Dank
>arum aus dem rjmp 1 ein RJMP PC-0x0001 wird
Die Frage ist eher, warum steht dort eine Eins???
Dort sollte ein Label stehen, welches dann als Sprungadresse gewertet
wird.
Ahhh, ich glaube, ich hab mein Problem falsch ausgedrückt.
Ich hätte erwartet, dass rjump 1 zu RJUMP PC+0x0001 und nicht zu
-0x0001 auf der Hardware wird. rjump 2 wird z.B. entsprechend zu
RJUMP-0x0000.
Also warum ändert AVR Studio das Argument. Der Offset ist übrigens auch
abhängig von dem Code davon/danach. An einer anderen Stelle im Code
werden diese Zeilen zu etwas anderem auf der Hardware compiliert.
Eigentlich erwartet (R)JMP ja eine absolute Adresse, die dann vom
Assembler entsprechend umgesetzt wird.
RJMP PC+1 sollte daher richtiger sein als RJMP 1.
Andre Stollenwerk wrote:
> lt. Datenblatt erwartet rjmp den relativen Offset. der PC ist danach PC> = PC + k + 1, wobei k das Argument ist.
Schau mal in die Befehlssatzdoku. Da steht ziemlich eindeutig, dass im
Assembler Labels verwendet werden, die automatisch in einen relativen
Offset umgewandelt werden. M.a.W.: Der Assembler bastelt sich das k
selber.
Was hindert Dich eigentlich daran, ein Label zu verwenden? Einfach vor
das nop ein entsprechendes Label, und dann das Argument vom rjmp
entsprechend anpassen.
Benedikt K. wrote:
> Eigentlich erwartet (R)JMP ja eine absolute Adresse, die dann vom> Assembler entsprechend umgesetzt wird.> RJMP PC+1 sollte daher richtiger sein als RJMP 1.
Im Prinzip richtig, aber auf PC kann man so nicht zugreifen. Der
Assembler interpretiert 1 als absolute Adresse. Da der rjmp an der
Stelle 0x0002 steht, muss dementsprechend um "-1" gehopst werden, um an
die Stelle "1" zu gelangen.
Ich rate aus eigener böser Erfahrung von der
Verwendung von PC+x ab, auch wenn Assembler
meine Welt ist.
Die Verwendung von Label kostet nichts.
Das angeführte Beispiel bestätigt genau dies.
Johannes M. wrote:
> Andre Stollenwerk wrote:>> lt. Datenblatt erwartet rjmp den relativen Offset. der PC ist danach PC>> = PC + k + 1, wobei k das Argument ist.> Schau mal in die Befehlssatzdoku. Da steht ziemlich eindeutig, dass im> Assembler Labels verwendet werden, die automatisch in einen relativen> Offset umgewandelt werden. M.a.W.: Der Assembler bastelt sich das k> selber.>> Was hindert Dich eigentlich daran, ein Label zu verwenden? Einfach vor> das nop ein entsprechendes Label, und dann das Argument vom /rjmp/> entsprechend anpassen.
Das ganze soll als Inline ASM unter C genutzt werden, und der will das
da mit einem weiteren Label einfach nicht kompilieren. Der o.g. Code war
das Minimalbeispiel...
>> Benedikt K. wrote:>> Eigentlich erwartet (R)JMP ja eine absolute Adresse, die dann vom>> Assembler entsprechend umgesetzt wird.>> RJMP PC+1 sollte daher richtiger sein als RJMP 1.> Im Prinzip richtig, aber auf PC kann man so nicht zugreifen. Der> Assembler interpretiert 1 als absolute Adresse. Da der rjmp an der> Stelle 0x0002 steht, muss dementsprechend um "-1" gehopst werden, um an> die Stelle "1" zu gelangen.
OK, dann ist das wohl des Rätsels Lösung, aber wie mache ich dann einen
wirklichen relativen Jump, also einfach eine Zeile überspringen, geht
das dann nur mit Labels??
Johannes M. wrote:
> Benedikt K. wrote:>> Eigentlich erwartet (R)JMP ja eine absolute Adresse, die dann vom>> Assembler entsprechend umgesetzt wird.>> RJMP PC+1 sollte daher richtiger sein als RJMP 1.> Im Prinzip richtig, aber auf PC kann man so nicht zugreifen. Der> Assembler interpretiert 1 als absolute Adresse. Da der rjmp an der> Stelle 0x0002 steht, muss dementsprechend um "-1" gehopst werden, um an> die Stelle "1" zu gelangen.
Ich habe das selbst noch nie gemacht, aber ELM Chan verwendet das
überall. Und bei dem scheint das zu funktionieren...
http://elm-chan.org/cc_e.html
Hallo Andre,
bei der Offset-Berechnung für PC+k
findest Du Dich unmittelbar in der Abzählerei
von Instruktions-Code-Größen wieder.
Und wehe, es wird ein rjmp in ein jmp geändert...
Seit 1997 mit AVR in Assembler zugange - mein Rat:
nimm Label!
Anders als bei den skip-Befehlen,
überspringst Du mit PC+k eben nicht Zeilen,
sondern addierst zu dem aktuellen Programmzähler
einen Offset in Wortlänge.
Wenn Du die zu überspringenden Instruktionen
nicht exakt in ihrer Wortlänge ausrechnest,
landest Du im Code-Nirwana. Salopp ausgedrückt.
Früher oder später passiert das und dann gibt
es graue Haare en masse. :-)
>aber wie mache ich dann einen>wirklichen relativen Jump, also einfach eine Zeile überspringen, geht>das dann nur mit Labels??
Nein, es geht zumindest im Assembler mit "rjmp PC+1". Analog "rjmp
PC+38" zum relativen Überspringen der folgenden 38 Instruktionen. Wie
hier schon angemerkt wurde erweist sich diese Notation in der Praxis
aber als sehr fehleranfällig. Oft schiebt man mal probeweise eine
Instruktion irgendwo rein. Übersieht man dann, dass dieser Teil
zufällig von einem "rjmp PC+n" übersprungen wird, und versäumt, das n
fünf Zeilen weiter oben auf n+1 zu ändern, dann springt der rjmp um eins
zu kurz. Die Ursache für das seltsame Verhalten des Programms, das dies
wahrscheinlich zeitigt, wird man jedoch fatalerweise bei der
eingeschobenen Instruktion suchen.
Deshalb: Immer Sprungmarken verwenden, denn dann berechnet der Assembler
die erforderlichen Sprungadressen bzw. -weiten.
Zusatz: Einige wenige Instruktionen, darunter "sts" und "lds", belegen
im Programmspeicher (Flash) nicht nur ein Wort (16 Bit), sondern zwei.
Die sind dann bei der Berechnung des n in einem "rjmp PC+n" auch mit 2
zu berücksichtigen! Es ist also genaugenommen nicht die reine Anzahl
der Instruktionen relevant, sondern ihr Platzbedarf im Programmspeicher.
Zum Überspringen dreier aufeinanderfolgender "sts"-Instruktionen wäre
"rjmp PC+6" korrekt.
Benedikt K. wrote:
> Ich habe das selbst noch nie gemacht, aber ELM Chan verwendet das> überall. Und bei dem scheint das zu funktionieren...
Ah, das war mir neu. Tja, man lernt nie aus...