Zeile 92: | Zeile 92: | ||
Also macht man sowas mit "die Adresse von der Adresse" (C-Programmierer kennen das). D.h. wir definieren ein WORD, schreiben die aktuelle Datenquell-Adresse rein, und unsere Funktion kriegt immer die Adresse von diesem Word. | Also macht man sowas mit "die Adresse von der Adresse" (C-Programmierer kennen das). D.h. wir definieren ein WORD, schreiben die aktuelle Datenquell-Adresse rein, und unsere Funktion kriegt immer die Adresse von diesem Word. | ||
<pre> | <pre> | ||
− | Dim | + | Dim Source As Word |
− | + | ||
− | + | Source = Varptr(source_string) ' Die SRAM-Adresse von "Source_String" | |
+ | </pre> | ||
− | + | ===Beispiel Hauptprogramm=== | |
+ | (für einen Atmeg32, das ist aber völlig egal). Ein paar Dinge sind noch dabei, damit man mit "print" auch im Simulator-Terminal die Sache kontrollieren kann. | ||
− | + | <pre> | |
+ | $regfile = "m32def.dat" | ||
+ | $crystal = 8000000 | ||
− | Ziel_len = Mem_move(ziel_arr(1) , Source , 5) | + | $baud = 9600 |
− | ' | + | $hwstack = 64 |
+ | $swstack = 256 | ||
+ | $framesize = 64 | ||
+ | |||
+ | |||
+ | Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte | ||
+ | |||
+ | |||
+ | Dim Ziel_len As Byte ' da soll das Ergebnis (Länge) rein | ||
+ | Dim Ziel_arr(24) As Byte ' hier soll hinkopiert werden. | ||
+ | |||
+ | Dim Print_string As String * 24 At Ziel_arr Overlay ' das ist nur für den "PRINT", um das | ||
+ | ' Zielarray auf einfach herzuzeigen | ||
+ | |||
+ | Dim Source_string As String * 24 ' das werden die Ziel-daten | ||
+ | |||
+ | Source_string = "Hello, world" ' damit auch was drinsteht | ||
+ | |||
+ | Dim Source As Word ' siehe oben | ||
+ | |||
+ | Source = Varptr(source_string) | ||
+ | |||
+ | Ziel_len = Mem_move(ziel_arr(1) , Source , 5) ' der funktionsaufruf: | ||
+ | ' 5 Byte von Source kopieren | ||
+ | |||
+ | Print "From " ; Source_string ' so sieht der Source-String aus | ||
+ | Print "To (" ; Str(ziel_len) ; ") " ; Print_string ' und so die kopierten Daten | ||
+ | |||
+ | End ' keine Schleife, wozu auch ? | ||
</pre> | </pre> | ||
+ | ===Funktion, Step 1: Inline Assembler=== | ||
+ | |||
+ | Erstmal komplett, zum bewundern: | ||
+ | <pre> | ||
+ | Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte | ||
+ | $asm | ||
+ | ldd xl, y + 0 'length | ||
+ | ldd xh, y + 1 'length | ||
+ | ld r24, x | ||
+ | clr r25 'clear counter | ||
+ | !and r24, r24 'length = 0 ? | ||
+ | breq Mem_mov_xit ' no action | ||
+ | |||
+ | ldd xl, y + 2 'source PARAM | ||
+ | ldd xh, y + 3 'source PARAM | ||
+ | ld zl, x+ 'source | ||
+ | ld zh, x 'source | ||
+ | |||
+ | ldd xl, y + 4 'target | ||
+ | ldd xh, y + 5 'target | ||
+ | |||
+ | Mem_mov_lop: | ||
+ | ld r22, Z+ 'read source | ||
+ | st x+, r22 'write target | ||
+ | inc r25 'counter++ | ||
+ | dec r24 'length-- | ||
+ | brne Mem_mov_lop '<> 0 ? -->loop | ||
+ | |||
+ | Mem_mov_xit: | ||
+ | ldd xl, y + 6 ' result | ||
+ | ldd xh, y + 7 ' result | ||
+ | st x, r25 | ||
+ | $end Asm | ||
+ | End Function | ||
+ | </pre> | ||
+ | |||
+ | '''Die Funktions Argumente:''' | ||
+ | |||
+ | Nochmals der Verweis auf den [[Bascom_Inside#Call_Sub_.26_Function|Bascom Call-Standard]]. Am Anfang der Funktion findet diese ihre Argumente im "SoftStack", das Pointer-Registerpaar Y (YL:YH) zeigt auf den Beginn. | ||
+ | Bascom legt die Addressen der Argumente absteigend in folgender Reihenfolge auf den Soft-Stack: | ||
+ | Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte | ||
+ | Erst die Adresse vom Funktionsergebnis, dann von links beginnend die Argumente in der Klammer, also | ||
+ | 1 Result Msb. | ||
+ | 2 Result Lsb | ||
+ | 3 Target Msb | ||
+ | 4 Target Lsb | ||
+ | 5 Srcaddr Msb | ||
+ | 6 Srcaddr Lsb | ||
+ | 7 Length Msb | ||
+ | 8 Length Lsb | ||
Version vom 4. Juni 2006, 14:58 Uhr
Inhaltsverzeichnis
Bascom Libraries
Im Installationsbereich von Bascom befinden sich eine Reihe Libraries mit den Endungen ".LBX" und ".LIB". Erstere sind gleichnamige, aber vorkompilierte Versionen von den ".LIB" Files. Dieses Vorkompilieren geschieht im LIB-Manager (Menu->Tools).
Aber auch die ".LBX" Files sind keine Object-Libraries, wie man sie von anderen Sprachen her kennt und die durch einen "Linker" zusammengefügt werden. Beides sind normale Textfiles, die erst beim Kompilieren des Basic-Hauptprogrammes tatächlich in Maschinencode übersetzt werden.
Deshalb ist es Bascom auch ziemlich egal, ob man bei der $lib Anweisung "lib.lib" oder "lib.lbx" angibt, es ist im Grunde die selbe Arbeit
Warum nun überhaupt ".LBX" ?
Durch das Vorkompilieren werden die Libraries doch einigermassen unleserlich, und wenn von einer Library die ".LIB" Version nicht da ist, will man den Code wohl nicht preisgeben.
Beide Versionen sind Sammlungen von "Bausteinen", die Bascom nur in das Benutzer-Programm einbindet, wenn sie tatsächlich gebraucht werden. Diese Bausteine sehen folgendermassen aus:
[Baustein_Name] Baustein_Name: .... Assembler-Answeisungen... RET [end]
Beispiel
Man erzeugt mit Notepad oder einem anderen Editor eine Datei "Testlib.LIB" im Library-Verzeichnis von Bascom (muß man im Installationsverzeichnis nachsehen, wo das ist)
[Meine_Funktion] Meine_Funktion: RET [end]
(und speichern, natürlich)
Im Hauptprogramm (in BasCom) schreibt man nun
$regfile = "m32def.dat" $crystal = ..... $lib "testlib.lib" $external Meine_Funktion Declare Sub Meine_Funktion() '.... irgendwo... CALL Meine_Funktion
Dadurch wird die Funktion an die nächste freie Stelle in den Maschinen-Code eingefügt und bei "Call" aufgerufen.
Man braucht jetzt nur noch irgendetwas Sinnvolles in die Funktion reinzuschreiben.
Ersetzen von Bascom-Library-Functions
Es ist sehr einfach, Funktionen, die sich in irgendeiner mitgelieferten Bascom-Library befinden, durch eigenen Code zu ersetzen. Bascom geht nämlich beim Suchen nach einer gerade benötigten Funktion im in der Reihenfolge der "$LIB" Anweisung vor. Die Standardlibrary "MCS.LBX" wird immer erst als Letztes durchsucht. Schreibt man also
$LIB "testlib.lib"
sucht er erst in dieser Library, und erst wenn da nix drinsteht, nimmt er das, was sich in MCS.LIB befindet.
Leider sind nicht alle Bascom-Funktionen in Libraries vorhanden, vieles ist im BasCom-Programm fest eingebaut. Und manches findet man erst nach ein wenig Detektivarbeit ("PULSEIN" heißt zum Beispiel "_PULSE_IN").
Tips
Vorgangsweise
Wenn man eine Library-Funktion entwickeln will, ist es günstig, sie erstmal als normale Sub-Routine mit inline-Assembler zu schreiben, und erst, wenn das halbwegs klappt, sie in eine Library zu übertragen. Die Syntax ist zwar etwas verschieden, aber das kriegt man hin. Der Vorteil ist aber, daß man seinen Assemblercode im Simulator normal debuggen kann, und das ist was wert.
Argumente
Wenn man der Funktion Argumente oder eine Result-Adresse übergeben will, muß man natürlich den Bascom Call-Standard beachten. Der ist aber recht aufwendig. Alle (globalen) Variablen, die man mit DIM angelegt hat, kann man auch mit "LOADADR" adressieren, das spart Einiges.
Sichern
Zwischen "$ASM" und "$END ASM" ist man Herrscher über den kompletten Controller. Man kann alle Register hemmungslos einsetzen. Es ist also nicht so wie in einer ISR, daß man alles sichern und wieder herstellen muß
Nur die Register
YL:YH R4:R5 R6
sollte man sichern und am Ende wieder herstellen, wenn man sie verändern will.
Tutorial:Eine Library-Funktion erstellen
Es soll der ganze Vorgang der Entwicklung einmal durchgezogen werden.
Mem_move
Diese Beispiel-Funktion soll einfach ein SRAM-Bereich in ein anderes kopieren, das Ziel ist immer ein Byte-Array, die Quelle hingegen sollen Daten mit beliebigen Datentypen sein. Die Übertragungslänge muss man daher natürlich auch angeben. Als Ergebnis wiederum die Anzahl der übertragenen Bytes. Das ist in diesem Beispiel natürlich sinnlos, aber wir wollen ja auch sehen, wie man einen Wert zurückgeben kann.
Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte
Soweit alles klar, "Target" ist das Zielarray, "Length" die Übertragungslänge, nur was hat es mit "Srcaddr As Word" auf sich ? Es sollen ja beliebige Daten und nicht nur ein WORD übertragen werden.
Das ist ja ein Haken bei Bascom, das geht eben nicht so direkt.
Wir müssen die Funktion-Deklaration ja mit einem konkreten Datentyp befüllen, und den Typ "irgendwas" gibt's eben nicht.
Also macht man sowas mit "die Adresse von der Adresse" (C-Programmierer kennen das). D.h. wir definieren ein WORD, schreiben die aktuelle Datenquell-Adresse rein, und unsere Funktion kriegt immer die Adresse von diesem Word.
Dim Source As Word Source = Varptr(source_string) ' Die SRAM-Adresse von "Source_String"
Beispiel Hauptprogramm
(für einen Atmeg32, das ist aber völlig egal). Ein paar Dinge sind noch dabei, damit man mit "print" auch im Simulator-Terminal die Sache kontrollieren kann.
$regfile = "m32def.dat" $crystal = 8000000 $baud = 9600 $hwstack = 64 $swstack = 256 $framesize = 64 Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte Dim Ziel_len As Byte ' da soll das Ergebnis (Länge) rein Dim Ziel_arr(24) As Byte ' hier soll hinkopiert werden. Dim Print_string As String * 24 At Ziel_arr Overlay ' das ist nur für den "PRINT", um das ' Zielarray auf einfach herzuzeigen Dim Source_string As String * 24 ' das werden die Ziel-daten Source_string = "Hello, world" ' damit auch was drinsteht Dim Source As Word ' siehe oben Source = Varptr(source_string) Ziel_len = Mem_move(ziel_arr(1) , Source , 5) ' der funktionsaufruf: ' 5 Byte von Source kopieren Print "From " ; Source_string ' so sieht der Source-String aus Print "To (" ; Str(ziel_len) ; ") " ; Print_string ' und so die kopierten Daten End ' keine Schleife, wozu auch ?
Funktion, Step 1: Inline Assembler
Erstmal komplett, zum bewundern:
Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte $asm ldd xl, y + 0 'length ldd xh, y + 1 'length ld r24, x clr r25 'clear counter !and r24, r24 'length = 0 ? breq Mem_mov_xit ' no action ldd xl, y + 2 'source PARAM ldd xh, y + 3 'source PARAM ld zl, x+ 'source ld zh, x 'source ldd xl, y + 4 'target ldd xh, y + 5 'target Mem_mov_lop: ld r22, Z+ 'read source st x+, r22 'write target inc r25 'counter++ dec r24 'length-- brne Mem_mov_lop '<> 0 ? -->loop Mem_mov_xit: ldd xl, y + 6 ' result ldd xh, y + 7 ' result st x, r25 $end Asm End Function
Die Funktions Argumente:
Nochmals der Verweis auf den Bascom Call-Standard. Am Anfang der Funktion findet diese ihre Argumente im "SoftStack", das Pointer-Registerpaar Y (YL:YH) zeigt auf den Beginn.
Bascom legt die Addressen der Argumente absteigend in folgender Reihenfolge auf den Soft-Stack:
Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte
Erst die Adresse vom Funktionsergebnis, dann von links beginnend die Argumente in der Klammer, also
1 Result Msb. 2 Result Lsb 3 Target Msb 4 Target Lsb 5 Srcaddr Msb 6 Srcaddr Lsb 7 Length Msb 8 Length Lsb