Mixing ASM and BASIC/de

From MCS Wiki AVR
Jump to: navigation, search

Contents

BASIC und Assembler mischen

 Dieses Kapitel darf nicht als Unterrichtung in Assemblerprogammierung verstanden werden. 
 Wenn Sie ein spezielles Thema zur Verwendung  von Assembler in BASCOM vermissen, teilen Sie uns das per Email mit.


BASCOM gestattet das mischen von BASIC mit Assembler-Code, was in den Fällen nützlich sein kann, in denen Sie vollen Einfluss auf den generierten Code nehmen wollen.

 

Nahezu alle Assembler-Mnemonics werden vom Compiler direkt unterstütz. Die Ausnahmen sind SWAP, CALL und OUT. Das sind reservierte BASIC-Befehle und haben Vorrang gegenüber Assembler-Mnemonics.

Um diese Mnemonics verwenden zu können wird ihnen ein Ausrufezeichen (!) vorangestellt.

 

noticeEs wird empfohlen immer das ! oder einen $ASM … $End ASM –Block zu verwenden.  


Beispiel :

 

   Dim a As Byte At &H60 'A is stored at location &H60
   Ldi R27 , $00   'Load R27 with MSB of address
   Ldi R26 , $60   'Load R26 with LSB of address
   Ld R1, X   'load memory location $60 into R1
   !SWAP R1   'swap nibbles

 

Wie Sie sehen, wurde dem Mnemonik SWAP ein Ausrufezeichen !  vorangestellt.

 

Eine andere Option ist die Verwendung eines einschließenden $ASM/ $END ASM - Blocks:

 

   $ASM
   LDI R27 , $00   'Load R27 with MSB of address
   LDI R26 , $60   'Load R26 with LSB of address
   LDI R1  , X    'load memory location $60 into R1
   SWAP R1   'swap nibbles
   $END ASM

 

Assembler-Hilfsfunktion

Eine spezielle Assembler-Hilfsfunktion unterstützt das Laden von Adressen in die Register X oder Z. Das Y-Register sollte nicht verwendet werden da dieses bereits als Zeiger auf den Software-Stack verwendet wird.

 

 Dim A As Byte  'reserve space
 LOADADR a, X  'load address of variable named A into register pair X

 

Nachfolgender Code hat den gleichen Effekt  :

 LDI R26 , $60 'for example !
 LDI R27, $00 'for example !

 

 

Von Bascom verwendete Register

R4 und R5 werden als Zeiger auf den Stack-Frame oder zur temporären Datensicherung verwendet.


R6 dient zur Speicherung von Bit-Variablen:

- bit 0 = Flag für Integer/Word Umwandlungen

- bit 1 = Temporärer Bit-Speicher beim Tauschen von Bits

- bit 2 = Fehler-Bit (ERR - Variable)

- bit 3 = Anzeigen/Nicht Anzeigen - Flag bei dem INPUT-Befehl


R8 und R9 dienen als Datenzeiger für den READ-Befehl.

 

Alle anderen Register werden Befehlsabhängig verwendet

 

Laden von Variablen-Adressen

Um die Adresse einer Variable zu laden, muß dessen Name in geschwungenen Klammern eingeschlossen sein.

   Dim B As Bit
   LDS R16, {B} '{B} wird durch die Adresse der Variablen B ersetzt. 

 

 

Um auf ein Bit zu verweisen, muss der Variablen der Ausdruck BIT. vorangestellt sein.  

   SBRS R16 , BIT.B  'Beachten sie den Punkt!

  Da B das erste dimensionierte Bit ist, erhält B die Bitnummer 0. Bits werden zu Bytes zusammengefasst gespeichert. Das erste dimensionierte Bit geht in das niederwertigste Bit ein, usw.

 

Laden von Label-Adressen

 

Um die Adresse eines Labels zu laden, schreiben sie :  

LDI ZL, Low(lbl * 1)
LDI ZH , High(lbl * 1)

 

wobei ZL auch R30, R24, R26, R28 oder R30, und ZH gleichermaßen R31, R25, R27, R29 oder R31 sein kann.

 

Das sind sogenannte Registerpaare mit denen Zeiger gebildet werden können.

 

 

Wenn Sie mit einer LPM – Instruktion (LOAD-PROGRAMM_MEMORY) Daten aus dem Programmspeicher laden wollen, muss die Adresse mit 2 multipliziert werden, da der Programmspeicher wortorganisiert ist (16Bit).


LDI ZL, Low(lbl * 2)
LDI ZH , High(lbl * 2)
LPM ; get data into R0
....
Lbl:
.....

Einige wichtige Punkte

 

 

Um in Assembler zu programmieren müssen Atmel Mnemonics verwendet werden. In den Datenblättern der jeweiligen Mikrocontroller die Sie unter www.atmel.com herunterladen können, finden sie den entsprechenden Befehlsvorrat.  


  • Alle Befehle die eine Konstanten verwenden, arbeiten ausschließlich mit den oberen 16 Registern R16 bis R31.

!LDI R15, 12 funktioniert also nicht.

 

 

  • Der Befehl SBR reg , K

K kann ein Wert von 0 bis 255 sein. So können mehrere Bits gleichzeitig gesetzt werden.

 

Mit dem Befehl SBI port , K hingegen kann immer nur ein Bit setzen, da K nur 0 bis 7 sein kann.  

Das Gleiche gilt für die Befehle CBR und CBI.

 

 

Sie können für K ebenfalls symbolische Konstante verwenden:

 

 .equ myval = (10+2)/4
 ldi R24,myval+2 '5
 ldi R24,asc("A")+1 ; load with 66

 

 

Oder in BASIC mit CONST :

 

 CONST Myval = (10+2) / 4
 LDI R24, myval

 

 

Bibliotheken (.lib) erstellen

Wie erstellt man eine eigene Bibliothek und verwendet diese mit BASIC?

Die Dateien zu diesem Beispiel finden sie im BASCOM-Installationsverzeichnis unter \SAMPLES\libdemo.bas und unter \LIB\mylib.lib.

 

 

Zu Beginn bestimmen Sie die zu verwendenden Parameter und ihre Datentypen.

Überlegen Sie außerdem ob die Parameter Byref oder Byval übergeben werden sollen.


- "ByRev" : Die Adresse der übergebenen Variable wird an eine Prozedur übergeben. So wird der Inhalt der Variable direkt bearbeitet.

- "ByVal" : Eine Kopie des Parameterinhaltes wird übergeben. Der Inhalt der übergebenen Quellvariable bleibt unangetastet.  

 

Beispiel:

Eine Subroutine namens "test" erwartet zwei Parameter:

 x die byval übergeben wird (eine Kopie. Kopien werden temporär in den Stack-Fame-Bereich geschrieben) 
 y die byref übergeben wird (die Adresse der Variablen)

   

In beiden Fällen wird die Adresse der Variablen auf den SoftStack gebracht, welcher mit dem Y-Zeiger verwaltet wird.  

 

Der erste Parameter x (genauer: die Adresse seiner Kopie) wird dem Soft-Stack auferlegt.

An diese Adresse gelangen sie durch:  

LDD R26 , Y + 0
LDD R27 , Y + 1

  Die Adresse steht nun im Registerpaar X (R26,R27) oder im X-Zeiger.

Da ein zweiter Parameter, oder besser dessen Adresse ebenfalls auf dem Soft-Stack gebracht werden soll, ändert sich jedoch die Verschiebegröße auf den Y-Zeiger um an die Adresse von x zu gelangen :    

x wird dadurch also so refenziert:

LDD R26 , Y + 2
LDD R27 , Y + 3

     

Ein zuletzt übergebenen Paramter (hier y) wird stets so refernziert:

LDD r26 , Y + 0
LDD r27 , Y + 1

 


Schreiben Sie eine gewöhnliche Subprozedur, zusätzlich jedoch mit dem Namen der Prozedur in eckigen Klammern, wie im folgendem Beispiel gezeigt.    

 [test]
   test:
    LDD R26,X+2 ; load address of x
    LDD R27,Y+3
    LD  R24,x ; get value into r24
    INC R24 ; value + 1
    ST X,R24 ; put back
    LDD R26,Y+0 ; address of y
    LDD R27,Y+1
    ST X,R24 ; store
   RET ; ready
 [end]

 

 

 

Um eine Funktion zu erstellen wird genauso verfahren.

Der Unterschied: eine Funktion gibt ein Ergebnis zurück, womit ein weiterer Parameter entsteht. Dieser Rückgabeparameter wird automatisch generiert und trägt den Namen der Funktion. Auf diese Weise kann das Ergebnis dem Funktionsnamen zugeordnet werden.  

Ein Beispiel:  

Declare Function Test(byval x as byte , y as byte) as byte

Eine virtuelle Variable namens Test wird erzeugt und auf den Soft-Stack mit Hilfe des Y-Zeigers gebracht.

 

Das Ergebnis der Funktion (test) würde an dieser Stelle so referenziert:

LDD R26 , Y + 0
LDD R27 , Y + 1

Durch den bei der Funktion übergebenen Parameter x jedoch, ändert sich die Verschiebegröße auf y und damit der Zugriff auf test zu

Y + 2 und Y + 3,

  und nachdem die Adresse des dritten Parameters y auf den Soft-Stack geschrieben wird, ändert sich der Verweis ebenfalls wieder:  

LDD R26 , Y + 4
LDD R27 , Y + 5

   

Schlussendlich wird auf die Adresse von x im Soft-Stack so verwiesen,

 LDD r26 , Y + 2
 LDD r27 , Y + 3

 

und auf y so.  

 LDD R26 , Y + 0
 LDD R27 , Y + 1

 

 

Für Exit Sub oder Exit Function muss außerdem ein zusätliches Label angegeben werden das mit sub_ beginnt und mit dem Namen der Funktion oder der Routine endet. Dem oben aufgeführten Beispiel entsprechend also:

 

 sub_test:

   

LOCALS

Wenn Sie mit lokalen Variablen arbeitet, wird es etwas komplizierter.  

Jede Adresse einer lokalen Variable wird auch auf den Soft-Stack geschrieben.  

Verwenden Sie nur eine lokale Variable, liegt dessen Adresse an  

 LDD R26, Y + 0
 LDD R27, Y + 1

  Die Referenzierung auf alle weiteren Parameter erhöht sich dadurch um 2, sodass sich der Zugiff auf die Adresse des Parameter y ändert.

 

LDD R26 , Y + 0   nach   LDD R26 , Y + 2
LDD R27 , Y + 1   nach   LDD R27 , Y + 3

     

Wenn mehr als eine lokale Variable verwendet werden, muss für jede Weitere 2 hinzuaddiert werden.  

Am Ende muss die Datei mit der Endung .lib abgespeichert werden. Mit dem Lib-Mangager kann die neue Bibliothek in das lbx-Format kompiliert werden.

Die jeweilige Declare Sub/Function Aweisung muss, um die Funktion oder Subroutine verwenden zu können, im Programmcode eingefügt werden.  

 

Das folgende Programmbeispiel ist eine Kopie aus der Datei libdemo.bas :    

 $lib "mylib.lib"                              'define the used library 
  
 $external Test                                'also define the used routines
  
 Declare Sub Test(byval X As Byte , Y As Byte) 'this is needed so the parameters will be placed correct on the stack
  
 Dim Z As Byte                                 'reserve some space
  
 Call Test(1 , Z)                              'call our own sub routine
  
 'z will be 2 in the used example
End

 

Wenn Sie in ihrer Bibliothek Ports verwenden wollen, müssen Sie für deren Adressen symbolische Konstanten anlegen. Beispiel:

 .equ EEDR=$1d
 In R24, EEDR

  So erkennt der Library-Manager Port-Adressen beim Kompilieren.   Alternativ können Sie dem Mnemonic ein * voranstellen. So wird diese Zeile beim Komilieren der Lib nicht mit übersetzt. Die Umsetzung der Adresse des Registers würde dann erst zur Laufzeit geschehen.

 


 

 

Umformung

  Ab Version 1.11.7.5 werden einige Mnemonics bei Bedarf anders umgesetzt, also umgeformt.  

Beispielsweise funktioniert SBIC nur mit normalen Port-Registern.  Der Hintergrund ist, daß die Adresse nicht größer als 5 Bit sein darf, da bereits 3 Bits schon für die Pin-Nummer 0-7 benötigt werden.   SBIC arbeitet nur mit den unteren 32 IO-Registern der Adressen 0-31.   SBIC funktioniert auf älteren AVR-Modellen problemlos. Bei der Anwendung des Befehls auf neuere Modelle, bei denen die Portadressen höher sein können, z.B. beim ATMega128 - PortG, versagt dieser Befehl.     Es wird, wo immer die Bits eines IO-Register manipuliert werden sollen, ein Arbeitsregister R[0-31] benötigt.   Beispiel :

LDS R23,   PORTG ; get value of PORTG register
SBR R23,   128 ; set bit 7
STS PORTG, R23

 

Mneminics die vom Compiler umgeformt werden,sind : IN, OUT, SBIC, SBIS, SBI and CBI.    Der Compiler verwendet dafür immer das Register R23. Achten Sie darauf, daß dieses Register in solch einem Fall nicht zeitgleich für etwas anderes verwendet wird.

 

Spezielle Befehle

ADR Label  ; Erzeugt eine Wortadresse einer Programmarke (Label)
ADR2 Label ; Erzeugt eine Wortadresse einer Programmarke (Label) die mit 2 multipliziert ist um ein Byte zu adressieren.  
             So lässt sich das Z-Register bequem zum gebrauch von(E)LPM-Befehlen laden.
 .align  ; Diese Direktive richtet den Programmcode an eine Seitengrenze des Programmspeichers (der in 256 Byte großen Blöcken aufgeteilt ist)so aus,
           daß das niederwertige Byte (LSB) einer Adresse zu 0 wird.
           Wenn Daten an eine bestimmte Adresse geschrieben werden bei dem das LSB = 0 ist, kann ein Überlauf nur durch Prüfung des MSB festgestellt werden.  


Languages   English Deutsch  
Personal tools
Namespaces
Variants
Actions
Navigation
In other languages
Language