PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : ICALL und IJMP in BascomAVR



Mitch64
22.03.2009, 09:27
Hallo an alle AVR'ler

Ich programmiere in BascomAVR 1.11.9.3 und scheitere an einer Inline-Assembler-Routine mit dem ASM-Befehl ICALL.

Hierzu habe ich folgenden Code geschrieben:


$regfile = "m48def.dat" ' ATmega48, Alternativ: ATmega88 o. ATmega168
$hwstack = 32
$swstack = 32
$framesize = 32
$crystal = 20000000 ' CPU kann 20MHz

Dim SubRoutine as word

SubRoutine = Loadlabel(Test_Routine1)
print hex(SubRoutine)

on int0 ISR_Horizontal nosave
enable int0
enable interrupts

do
nop
loop

' Horizontal-Interrupt
ISR_Horizontal:
push r24 ' r24 sichern
lds r24,sreg ' Statusregister nach r24
push r24 ' Statusregister sichern
push r30 ' Register Z sichern
push r31
lds r30 , {SubRoutine} ' Adresse von SubRoutine nach Z
lds r31 , {SubRoutine+1}
Icall ' SubRoutine aufrufen
pop r31 ' Z-Register restaurieren
pop r30
pop r24 ' Statusregister vom Stack holen
sts sreg,r24 ' Statusregister restaurieren
pop r24 ' r24 restaurieren
Return

Test_Routine1:
nop
Return

Test_Routine2:
nop
Return



Kurz zur Erklärung was ich bezwecken will:

In einer zeitkritischen Interruptroutine (hier stellvertretend INT0) mochte ich auf verschiedene Unter-Routinen verzweigen. Hierzu habe ich eine Word-Variable dimensioniert, die die Adresse enthält, auf die in der Interrupt-Routine verzweigt werden soll.

In der Interrupt-Routine selbst lade ich nun das Z-Register (r30 / r31) mit der Adresse und versuche den Call mit ICALL durchzuführen.

In der Simulation wird der Sprung auf die im Code angegebene Routine nicht durchgeführt. In der Statuszeile wird "Stopped" angezeigt. Beim weiter steppen in der Simulation beginnt das Programm bei Programmcounter (PC) Adresse 0.

Was mache ich falsch?

Lade ich die Register R24/R25 falsch oder funktioniert der Assembler-Befehl ICALL nicht in Bascom?

Habe es auch mit IJMP versucht, auch da stürzt das Programm ab.

Kann mir jemand von euch weiter helfen?

Gruß Mitch.

mare_crisium
22.03.2009, 10:34
Mitch,

an der Stelle musste ich auch lange knobeln ;-) ! Du musst den Zeiger verdoppeln, weil die Anweisungen bei den ATmegas immer zwei Bytes (14 Bit) belegen. Ich arbeite viel mit Adressentabellen und habe mir dafür eine extra Assembler-Prozedur geschrieben:



/*------------------------------------
PROCEDURE TBL_CALL

Die Prozedur TBL_CALL liest aus einer Adressentabelle, die
im Programmspeicher abgelegt ist, ein 2-Byte-Adresse aus.
Sie wird als Programmadresse angesprungen. Das aufrufende
Programm ist selbst dafür verantworlich, dass der maximale
Index nicht überschritten wird. Das aufrufende Programm
wird in der Zeile nach der Anweisung "rcall TBL_CALL "
fortgesetzt.


Eingangsvariablen
zh:zl: enhält den Zeiger auf die RAM-Adresse,
an der die Sprungtabelle beginnt
r16: enthält den Index der Adresse, die
angesprungen werden soll

Ausgangsvariablen
keine

geänderte Register
zh,zl

geänderte Ports
keine

*/

TBL_CALL:
push r17
in r17,SREG
push r17


clr r17
add zl,r16
adc zh,r17
add zl,zl
adc zh,zh

lpm r17,z+
lpm zh,z
mov zl,r17

pop r17
out SREG,r17
pop r17

ijmp

ret


Die Anwendung im Programm sieht so aus:



...
PROGRM_TABELLE:
.dw TU_DIES
.dw TU_DAS
.dw TU_JENES
...
...
ldi zl,low(PROGRM_TABELLE) ; Zeiger auf Tabelle nach zh:zl
ldi zh,high(PROGRM_TABELLE)
ldi r16,0x02 ; Programmteil "TU_JENES" auswählen
rcall TBL_CALL
nop
...

TU_JENES:
nop
ret
...



Das Auslesen der Unterprogramm-Startadresse aus dem Flash geht mit der Anweisung "lpm". Da ich das ausgewählte Unterprogramm in TBL_CALL mit "ijmp" anspringe, bleibt die Rückkehradresse, die noch vom Aufruf von "rcall TBL_CALL" auf dem Systemstack liegt, unverändert. D.h. nach Abarbeiten von "TBL_CALL" und des Unterprogramms ("TU_JENES"), kehrt der Programmablauf zu der nächsten, auf "rcall TBL_CALL" folgenden Anweisung (nop) zurück.

Klappt's so?

mare_crisium

Mitch64
22.03.2009, 10:46
Hallo mare_crisium,

danke erst mal für den Tip. Aber checken tu ich das gerade nicht.
Mir ist aufgefallen, dass der Wert in der Variablen SubRoutine nicht der Adresse der Sprungsdresse des Labels entspricht.

Mit anderen Worten ich springe irgend wo hin aber nicht da wo es hin gehen sollte.

Wie verdopple ich die Sprungsdresse?
Einfach mal 2?

komme grad nicht klar damit!

Mitch64
22.03.2009, 10:50
mit LoadLabel ermittle ich die Adresse des Labels. Bei mir Adresse: 0x00c8

Die Routine liegt aber bei Adresse: 0x0064.

Wie korrigiere ich das?

Mitch64
22.03.2009, 12:02
Nach etwas rumprobieren bin ich nun zu volgendem Schluss gekommen:

Die Label-Adresse, die mit LoadLabel ermittelt wird muss nicht verdoppelt, sondern halbiert werden.

Jetzt bleibt die Frage, wie ich das Z-Register (16 bit) nach rechts um 1 Bit verschiebe.

Kann mir das noch jemand sagen?

Mitch.

PicNick
22.03.2009, 12:08
LSR R31
ROR R30
:-)

Mitch64
22.03.2009, 12:20
Danke, muss ich gleich mal testen.

Mitch64
22.03.2009, 12:34
Suppi!

Danke, mein Code funktioniert.

Für alle, die es interessiert, der Code sieht jetzt so aus!


$regfile = "m48def.dat" ' ATmega48, Alternativ: ATmega88 o. ATmega168
$hwstack = 32
$swstack = 32
$framesize = 32
$crystal = 20000000 ' CPU kann 20MHz

Dim SubRoutine as word ' Adresse Subroutine (wird in ISR_Horizontal aufgerufen)

SubRoutine = Loadlabel(Test_Routine2) ' Adresse Sub-Routine setzen

print hex(SubRoutine)
!call Test_Routine1

'SubRoutine = SubRoutine \ 2
print hex(SubRoutine)

on int0 ISR_Horizontal nosave
enable int0
enable interrupts

do
nop
loop

' Horizontal-Interrupt
ISR_Horizontal:
push r24 ' r24 sichern
lds r24,sreg ' Statusregister nach r24
push r24 ' Statusregister sichern
push r30 ' Register Z sichern
push r31
lds ZL , {SubRoutine} ' Adresse von SubRoutine nach Z
lds ZH , {SubRoutine+1}
LSR ZH ' R31
ROR ZL ' R30
Icall ' SubRoutine aufrufen
pop r31 ' Z-Register restaurieren
pop r30
pop r24 ' Statusregister vom Stack holen
sts sreg,r24 ' Statusregister restaurieren
pop r24 ' r24 restaurieren
Return

Test_Routine1:
nop
Return

Test_Routine2:
nop
Return

s.o.
22.03.2009, 16:14
wenn du noch was perfektionieren willst, pushste und popste auch das SREG.

PicNick
22.03.2009, 17:50
Sag ich ja: Kaum macht man was richtig, geht's auch schon *g*