5. Tip's in Verbindung mit dem Inline-Assembler (Teil 1)
Kurzübersicht:
5.1. Funktionsprinzip des PowerBASIC Inline-Assemblers
5.2. Assembler Syntax Error
5.3. Fehlerhafte Variablenübergabe im Inline-Assembler
5.4. Probleme mit LDS/LES
5.5. Absturz nach Aufruf einiger INT-Funktionen
5.6. Fixup Overflow
5.7. Variablen zerlegen von WORD nach BYTE
5.8. Variablen zerlegen von DWORD nach WORD
5.9. Zugriff mit dem Inline-ASM auf Array's/Strukturen
5.10. Parameterrückgabe mit dem Inline-ASM
5.11. Variablenübergabe in Interrupt-Prozeduren
5.12. Erstellen von 32bit-Zeigern
5.13. Konvertierung von REG nach Inline-ASM
5.14. Konvertierung von A86 nach Inline-ASM
Die hier beschriebenen Tip's sollen keinen Grundlehrgang im Inline-Assembler
darstellen, sondern nur die wichtigsten Anfängerprobleme
umschiffen zu helfen. Auf eine genauere Beschreibung der Zusammenhänge
wird deshalb verzichtet, es sei es gehört zum Problem selber.
5.1. Funktionsprinzip des PowerBASIC Inline-Assemblers
Der PowerBASIC Inline-Assembler besitzt den Funktionsumfang der Intel
8086 CPU. Das heißt, da Sie Inline-Assembler-Code anderer Hochsprachen-Compiler
bzw. reinen Assembler-Code leicht an den PowerBASIC Inline-Assembler
anpassen müssen, da diese recht oft bereits 80286'er Befehle
enthalten. In der Regel fallen immer wieder folgende Befehle auf, welche
Sie wie folgt konvertieren können:
(Listing)
Quell-Source - > PowerBASIC
shr ax, 2 ! shr ax, 1
! shr ax, 1
oder ähnlich wie folgt:
-----------------------------------------------------------
shl ax, 3 ! push cx
! mov cl, 3
! shl ax, cl
! pop cx
-----------------------------------------------------------
pusha ! push ax
! push bx
und so weiter bis alle Register ge-
sichert sind
-----------------------------------------------------------
popa analog, nur Register wieder restaurieren.
(Ende)
5.2. Assembler Syntax Error
Wenn wir von einem richtigen Syntaxfehler absehen, der in der Regel auftritt
wenn Sie noch nicht so recht mit den Assemblerbefehlen vertraut
sind, kann der Inline-Assembler von PowerBASIC auch einen 'scheinbaren'
Syntax Error erzeugen. Dies ist dann immer der Fall, wenn der Compiler
eine Variable im Inline-Assembler nicht auflösen kann, da Sie nicht in
irgendeiner Form definiert ist.
PowerBASIC legt normalerweise die verwendeten Variablen innerhalb einer
reinen BASIC-Source selbstständig an und weist dieser Variablen Ihren
Speicherplatz zu. Innerhalb des Inline-Assemblers müssen Sie dafür
selber sorgen.
Beispiel:
! mov ax, Demo%
führt automatisch zu einem Syntax-Error, da der Compiler nichts mit der
Variablen 'Demo%' anfangen kann. Sollten Sie der Variablen vorher einen
Wert zuweisen:
Demo% = 1
! mov ax, Demo%
dann akzeptiert der Compiler nun die Assemblerzeile. Sie müssen aber
nicht jedesmal die Variable speziell mit einem Wert laden, ein bloßes
DIM oder auch SHARED, PUBLIC, LOCAL etc. reicht hierbei völlig aus und
initialisiert 'Demo%'.
5.3. Fehlerhafte Variablenübergabe im Inline-Assembler
Oft haben Sie bestimmt schon geflucht, da eine funktionierende Routine
mit REG(x) nach der Umsetzung in den Inline-Assembler nicht mehr sauber
funktioniert oder wenn ebenso Ihre Testroutine nicht mehr in einer
SUB/FUNCTION ihre Arbeit verrichten will.
Des Rätsels Lösung ist relativ einfach: Sie müssen Variablen für den
Inline-Assembler immer BYVAL übergeben.
Beispiel:
Demo 1
FUNCTION Demo(BYVAL i%) public
! mov ax, i%
! inc ax
! mov i%, ax
PRINT i%
END FUNCTION
Dieses kleine Demo addiert per Inline-Assembler einfach den Wert '1'
und gibt diesen dann auf den Bildschirm aus. Lassen Sei einfach das
BYVAL in der Parameterübergabe weg und testen Sie das Demo erneut!
5.4. Probleme mit LDS/LES
Ähnlich wie bei der Parameterübergabe für den Inline-Assembler verhält
sich das Funktionsprinzip der Assemblerbefehle LDS/LES. Von entscheidener
Bedeutung ist hierbei ebenfalls ob Sie die Variable
'BY COPY', 'BY REFERENCE' oder auch 'BY VALUE' übergeben. Als Faustregel
können Sie sich merken:
BY REFERENCE: - eigentlich als default im Hautprogramm
- bzw. wenn Sie die Variable als SHARED/PUBLIC etc.
deklariert haben.
BY COPY: - default immer in einer SUB/FUNCTION, sofern Sie
die Variable nicht BY VALUE übergeben.
BY VALUE: - interpretiert der Inline-Assembler immer als BY
REFERENCE.
LDS/LES sollten Sie nur Variablen vom Typ BY COPY übergeben, da nur
hierbei in DS/ES die Segmentadresse geladen wird und im anderen Register
dann die Offsetadresse der Variablen.
Bei der Übergabe BY REFERENCE wird in DS/ES der höherwertige Inhalt der
Variablen geladen, sofern vom Typ Long/DWord, ansonsten enthält das
DS/ES-Register einen irrelevanten Wert. Im anderen Register befindet
sich dann der niederwertige Inhalt der Variablen.
Beispiel:
SHARED DemoSeg%, DemoOff%
i& = &h12345678
Demo1 i&
Demo2 i&
Demo3 i&
FUNCTION Demo1(i&) public
PRINT "PB-Adresse : ";:
PRINT HEX$(VARSEG(i&));":"; HEX$(VARPTR(i&))
END FUNCTION
FUNCTION Demo2(i&) public
! les bx, i&
! mov DemoSeg%, es
! mov DemoOff%, bx
PRINT "LES /BY COPY : ";:
PRINT HEX$(DemoSeg%);":"; HEX$(DemoOff%)
END FUNCTION
FUNCTION Demo3(BYVAL i&) public
! les bx, i&
! mov DemoSeg%, es
! mov DemoOff%, bx
PRINT "LES /BY VALUE: ";:
PRINT HEX$(DemoSeg%);":"; HEX$(DemoOff%)
END FUNCTION
5.5. Absturz nach Aufruf einiger INT-Funktionen
Warum dieser Abschnitt werden Sie sich fragen, hat der PowerBASIC
Inline-Assembler irgendwelche Fehler? Die Antwort lautet definitiv:
NEIN.
Trotzdem sind viel Aufrufe über INT-Funktionen des BIOS/DOS mit einiger
Tücke verbunden, da Sie wichtige Segmente verbiegen bzw. speziell
zuweisen müssen. Viele Buffer, die einer Funktion übergeben werden
müssen, erwarten speziell im Datensegment-Register (DS) Ihren Zeiger.
PowerBASIC selbst addressiert aber seine Variablen ebenfalls über DS,
sodas hier Konflikte vorprogrammiert sein können. So sollte es zum
Beispiel nicht sein:
! mov ax, &h3D90 ; Funktion Datei öffnen
! mov ds, FileSeg?? ; Segment des Dateinamens laden,
! mov dx, FileOff?? ; jetzt kommt der erste Fehler, da
; FileOff?? nicht mehr über DS
; addressiert werden kann. DS zeigt
; bereits woanders hin ...
! int &h21 ; INT-Aufruf
! mov Handle%, ax ; da DS immernoch für PowerBASIC ins
; Nirwana zeigt, schlägt diese Zeile
; ebenfalls fehl und PowerBASIC
; stürzt über kurz oder lang ab.
Ein sauberes Listing sollte wie folgt aussehen:
! push ds ; DS sichern
! mov ax, &h3D90
! mov dx, FileOff?? ; Offset des Dateinamens laden
! mov ds, fileSeg?? ; Segment des Dateinamens laden,
; für PowerBASIC brauchen wir es
; auch nicht mehr
! int &h21 ; INT-Aufruf
! pop ds ; PowerBASIC Segment restaurieren
! mov Handle%, ax ; Handle% (bzw. Fehlercode) zuweisen
! jnc ... ; Carry-Flag abfragen
5.6. Fixup Overflow
Das Problem ist recht einfach und simpel: Die 8086'er CPU lässt nur
bedingte Sprünge vom Typ SHORT zu, das heißt sie können direkt nur
Labels im Bereich von -127/+128 Opcodes Entfernung anspringen.
Folgendes Beispiel erzeugt also einen Fehler:
DemoLabel:
<mehr als 128 Bytes Opcode>
! jc DemoLabel
Um der Sache aus dem Weg zu gehen, müssen Sie die ganze Sache nur etwas
anders adressieren. Das ist im Prinzip kein Problem, man muss es nur
einmal wissen:
DemoLabel:
<mehr als 128 Bytes Opcode>
! jnc DemoWeiter
! jmp near DemoLabel
DemoWeiter:
Das steht allerdings auch in jedem Assembler-Buch ...
5.7. Variablen zerlegen von WORD nach BYTE
Sollten Sie immer noch Ihre 16bit Variablen mit mathematischen Aufwand
in Ihre 8bit Bestandteile zerlegen, so wird es Zeit das Sie endlich
damit aufhören. Das kann die CPU von ganz alleine erledigen:
Beispiel:
DIM Demo AS WORD
DIM DemoHigh AS BYTE
DIM DemoLow AS BYTE
Demo?? = &h1234
! mov ax, Demo??
! mov DemoLow? , al
! mov DemoHigh?, ah
5.8. Variablen zerlegen von DWORD nach WORD
Öfters steht man vor dem Problem, das man zwar Zeiger und Variablen vom
Typ DWORD in PowerBASIC hat, allerdings nicht weis wie man diese im
Inline-Assembler übergeben kann oder je nach Bedarf auch in WORD zer-
legen. Im Prinzip ist auch diese recht einfach (gewußt wie):
Beispiel:
DIM Demo AS DWORD
DIM DemoHigh AS WORD
DIM DemoLow AS WORD
Demo??? = &h12345678
! mov ax, Demo???[00]
! mov bx, Demo???[02]
! mov DemoLow??, ax
! mov DemoHigh??, bx
5.9. Zugriff mit dem Inline-ASM auf Array's/Strukturen
Relativ einfach ist der Zugriff mit dem Inline-Assembler auf feste
Datenstrukturen, sofern sie bereits vorher die genauen Offsetadressen
Adressen wissen.
PowerBASIC erlaubt Ihnen folgenden Syntax:
Beispiel:
! mov ah, byte ptr es:[di][22]
Kopiert den Wert zum Offset 22 der Adresse ES:DI in das AH-Register.
Tip's in Verbindung mit dem Inline-Assembler (Teil 2)