-         

Ergebnis 1 bis 10 von 10

Thema: ICALL und IJMP in BascomAVR

  1. #1
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    12.07.2008
    Ort
    Villingen-Schwenningen
    Beiträge
    143

    ICALL und IJMP in BascomAVR

    Anzeige

    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:

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

  2. #2
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    08.12.2005
    Beiträge
    535
    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:

    Code:
    /*------------------------------------
    	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:

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

  3. #3
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    12.07.2008
    Ort
    Villingen-Schwenningen
    Beiträge
    143
    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!

  4. #4
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    12.07.2008
    Ort
    Villingen-Schwenningen
    Beiträge
    143
    mit LoadLabel ermittle ich die Adresse des Labels. Bei mir Adresse: 0x00c8

    Die Routine liegt aber bei Adresse: 0x0064.

    Wie korrigiere ich das?

  5. #5
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    12.07.2008
    Ort
    Villingen-Schwenningen
    Beiträge
    143
    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.

  6. #6
    Super-Moderator Robotik Visionär Avatar von PicNick
    Registriert seit
    23.11.2004
    Ort
    Wien
    Beiträge
    6.836
    LSR R31
    ROR R30
    mfg robert
    Wer glaubt zu wissen, muß wissen, er glaubt.

  7. #7
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    12.07.2008
    Ort
    Villingen-Schwenningen
    Beiträge
    143
    Danke, muss ich gleich mal testen.

  8. #8
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    12.07.2008
    Ort
    Villingen-Schwenningen
    Beiträge
    143

    Es funktioniert! Danke!

    Suppi!

    Danke, mein Code funktioniert.

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

    Code:
    $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

  9. #9
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    08.07.2006
    Ort
    Karlsruhe/München
    Alter
    27
    Beiträge
    587
    wenn du noch was perfektionieren willst, pushste und popste auch das SREG.

  10. #10
    Super-Moderator Robotik Visionär Avatar von PicNick
    Registriert seit
    23.11.2004
    Ort
    Wien
    Beiträge
    6.836
    Sag ich ja: Kaum macht man was richtig, geht's auch schon *g*
    mfg robert
    Wer glaubt zu wissen, muß wissen, er glaubt.

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •