Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


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 ?

Step 1: Inline Assembler

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 Argument-Adressen in der Klammer, also insgesamt:

  • High(Result)
  • Low(Result)
  • High(Target)
  • Low(Target)
  • High(Srcaddr)
  • Low(Srcaddr)
  • High(Length)
  • Low(Length)

Und auf diesen letzten Wert zeigt YL:YH beim Aufruf der Funktion. Gelesen wird das mit

LD   register, Y + Offset

Ein Beispiel:

   ldi    r24, 65
   ldd    xl, y + 6   
   ldd    xh, y + 7 
   st     x, r24

Entspricht dem Bascom-Statement

Mem_move = 65

d.h. die Funktion würde das Ergebnis 65 liefern

Und nun die Funktion "inline"

Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte
   $asm
   ldd    xl, y + 0                               'length addr lo
   ldd    xh, y + 1                               'length addr high
   ld     r24, x                                  'Length --> R24
   clr    r25                                     'Löschen Zählregister
   !and     r24, r24                              'length = 0 ?
   breq     Mem_mov_xit                           'wenn die Länge = 0 , ist garnix zu tun

Erst wir Xl:XH mit der adresse des Längenarguments geladen, dann Register 24 mit dem eigentlichen Wert. Der Zähler für das Funktionsergebnis wird auf Null gesetzt, dann wird die angegebene Länge geprüft, ob überhaupt was zu tun ist.

(Anm: das Rufzeichen beim "and" zeigt dem Bascom, daß das ein "Assembler-And" ist und ihn nix angeht)

   ldd    xl, y + 2                               'source PARAM
   ldd    xh, y + 3                               'source PARAM
   ld     zl, x+                                  'source
   ld     zh, x                                   'source

Für "Source" ist eine Ecke mehr: Auf dem Stack befindete sich die Adresse von einem WORD. Und erst da drin steht die eigentliche Adresse der Source-Daten.

   ldd    xl, y + 4                               'target
   ldd    xh, y + 5                               'target

Das ist der "Normalfall". Auf dem Stack steht direkt die Zieladresse ( Ziel_arr(1) )

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

Die Copy-Schleife: von Z mit autoinkrement ins Register 22, von da nach X, auch mit Inkrement. Jedesmal wir der Ergebniszähler erhöht und der Kontrollzähler vermindert.

Mem_mov_xit:
   ldd    xl, y + 6                               ' result
   ldd    xh, y + 7                               ' result
   st       x, r25
   $end Asm
End Function

X wird mit der Adresse des Funktionsergebnisses geladen (s.o) und R25 wird dorthingeschrieben.

Testen im Simulator Man sollte sich die tatsächlichen Variablen-Adressen aus der ".RPT" File anschauen. Damit und mit dem Register- und Memory-Fenster des Simulators kann man recht gut die einzelnen Steps beobachten. Für die Printout's beim "Teminal" ein Häkchen machen !


Step 2: Library Function

Angenommen, wir sind zufrieden, dann machen wir nun eine Library. Ich mach das mit Word- oder Notepad, ist egal, ein Text-Editor halt. Geht auch mit Bascom selbst, ist aber Geschmackssache

Eine leere Library

copyright = Anybody
www       = http://www.roboternetz.de
email     = picnick@nowhere.at
comment   = BASCOM AVR compiler library for Demo
libversion   = 0.1
date         = 04 jun 2006
history      = Beta

Und nun mit Cut & Paste aus dem Bascom Editor die Funktion einfügern

copyright = Anybody
www       = http://www.roboternetz.de
email     = picnick@nowhere.at
comment   = BASCOM AVR compiler library for Demo
libversion   = 0.1
date         = 04 jun 2006
history      = Beta

; Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte

[Mem_move]
Mem_move:
   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

  ret         ; ready
[end]

Das Rufzeichen bei "AND" lassen wir weg, dafür kommt ist das dazugekommen. Ich rate, die Funktions-Deklaration als Kommentar dazuzuschreiben.

[Mem_move]
Mem_move:
.....
 ret         ; ready
[end]

Das "RET" hat bei der Inline Version der Bascom gemacht ("END FUNCTION"), das darf man nun nicht vergessen.

Und nun "speichern als" RN_LIBRARY.LIB bei den anderen Bascom Libraries. Bei mir ist das z.B.

E:\Programme\MCS Electronics\BASCOM-AVR\LIB\RN_LIBRARY.LIB
Aufpassen mit dem File-Typ !

Wir können das nun schon kontrollieren, im Bascom bei "Tool" ist ein Lib-Manager, das muß diese Library nun zu sehen sein.


Hauptprogramm umbauen Das sieht nun so aus

$regfile = "m32def.dat"
$crystal = 8000000

$baud = 9600
$hwstack = 64
$swstack = 256
$framesize = 64

$lib "RN_Library.LIB"                 ' das ist neu 
$external Mem_move                    ' das ist neu 

Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte


Dim Ziel_len As Byte
Dim Ziel_arr(24) As Byte

Dim Print_string As String * 24 At Ziel_arr Overlay
Dim Source_string As String * 24

Dim Source As Word

   Source_string = "Hello, world"

   Source = Varptr(source_string)
   Ziel_len = Mem_move(ziel_arr(1) , Source , 5)

   Print "From " ; Source_string
   Print "To  (" ; Str(ziel_len) ; ") " ; Print_string

End

Wenn wir nun "F7" drücken, wird sowohl das Basic-Programm als auch die Library übersetzt.

Das war's eigentlich

LBX ?

Von wegen ".LBX": Wenn wir mit dem Lib-Manager die Library kompilieren lassen, sieht das Ergebnis so aus:

Comment = Compiled LIB file, no comment included

copyright = Anybody
www       = http://www.roboternetz.de
email     = PicNick@nowhere.at
comment   = BASCOM AVR compiler library for Demo
libversion   = 0.1
date         = 04 jun 2006
history      = Beta
[Mem_move]
Mem_move:
.OBJ 81A8
.OBJ 81B9
.OBJ 918C
.OBJ 2799
.OBJ 2388
   breq     Mem_mov_xit                           ' no action
.OBJ 81AA
.OBJ 81BB
.OBJ 91ED
.OBJ 91FC
.OBJ 81AC
.OBJ 81BD
Mem_mov_lop:
.OBJ 9161
.OBJ 936D
.OBJ 9593
.OBJ 958A
   brne  Mem_mov_lop                              '<> 0 ? -->loop
Mem_mov_xit:
.OBJ 81AE
.OBJ 81BF
.OBJ 939C
.OBJ 9508
[end]

Damit kann man sein know-how vor neugierigen Blicken verstecken. Um das zu verwenden, muß man im Hauptprogramm aber dann schreiben:

$LIB "RN_LIBRARY.LBX"

Anmerkung

Library-Funktionen können auch ohne den Call-Standard verwendet werden. D.h. die Sache mit der Argumenten-Übergabe über den Softstack ist nicht zwingend vorgeschrieben.

Man könnte das auch folgendermaßen machen und einigen Code sparen, den das Aufbauen des SoftStacks und das Lesen der Argumente in der Funktion selbst sind recht aufwendig.

In unserem Beispiel lief ja im Kern der Copy über die Pointer-register Z u. X mit dem Zähler r24

Hauptprogramm

$LIB = "library.lib"
$external move_z_to_x
Declare Sub move_z_to_x()

     $asm
     LDI    r24, 5
     $end asm
     LOADADR array(1), X         ' das werden nur 2 Maschinenbefehle !
     LOADADR source_string, Z    ' ebenso
     GOSUB  move_z_to_x          ' das ist einer


library.lib

...
[move_z_to_x]
move_z_to_x:
_mov_lop:
   ld    r22, Z+                                            'read source
   st    x+, r22                                            'write target
   dec   r24                                                'length--
   brne  _mov_lop                                           '<> 0 ? -->loop
   ret
[end]

Autor

Siehe auch