Mixing ASM and BASIC/de
(→LOCALS) |
(→BASIC und Assembler mischen) |
||
Line 32: | Line 32: | ||
<span style="font-family: Arial;"> </span> | <span style="font-family: Arial;"> </span> | ||
− | Wie Sie sehen, wurde dem | + | Wie Sie sehen, wurde dem Mnemonik SWAP ein Ausrufezeichen <span style="font-weight: bold;">!</span> vorangestellt. |
<span style="font-family: Arial;"> </span> | <span style="font-family: Arial;"> </span> | ||
− | + | Eine andere Option ist die Verwendung eines einschließenden $ASM/ $END ASM - Blocks: | |
<span style="font-family: Arial;"> </span> | <span style="font-family: Arial;"> </span> |
Revision as of 09:55, 5 March 2013
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.
Es 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 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 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 |
---|