5. Tip's in Verbindung mit dem Inline-Assembler (Teil 1)

English


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)


(c) 1995/2007 by Thomas Gohel, All rights and bug's reserved