GW-Basic Sous-programmes de langage assembleur (code machine)

PC-BASIC

Cette annexe s'adresse principalement aux utilisateurs expérimentés dans la programmation en langage assembleur.

GW-BASIC vous permet d'interfacer avec les sous-routines du langage d'assemblage en utilisant la fonction USR et l'instruction CALL.

La fonction USR permet aux sous-programmes du langage assembleur d'être appelés de la même manière que les fonctions intrinsèques GW-BASIC.

Cependant, l'instruction CALL est recommandée pour interfacer des programmes en langage machine avec GW-BASIC. L'instruction CALL est compatible avec plus de langages que l'appel de fonction USR, produit un code source plus lisible et peut transmettre plusieurs arguments.

Allocation de mémoire

L'espace mémoire doit être réservé pour un sous-programme en langage assembleur (ou code machine) avant de pouvoir le charger. Il existe trois méthodes recommandées pour réserver de l'espace pour les routines en langage assembleur :

Spécifiez un tableau et utilisez VARPTR pour localiser le début du tableau avant chaque accès.

Utilisez le commutateur /m dans la ligne de commande. Obtenez le segment de données (DS) de GW-BASIC et ajoutez la taille de DS pour référencer l'espace réservé au-dessus du segment de données.

Exécutez un fichier .COM qui reste résident et stockez un pointeur vers celui-ci dans un emplacement de vecteur d'interruption inutilisé.

Il existe trois méthodes recommandées pour charger des routines en langage assembleur :

BLOAD le fichier. Utilisez DEBUG pour charger un fichier .EXE qui est en mémoire haute, exécutez GW-BASIC et BSAVE le fichier .EXE.

Exécutez un fichier .COM contenant les routines. Enregistrez le pointeur vers ces routines dans des emplacements de vecteur d'interruption inutilisés, afin que votre application dans GW-BASIC puisse obtenir le pointeur et utiliser la ou les routines. Placez la routine dans la zone spécifiée.

Si, lorsqu'un sous-programme en langage assembleur est appelé, plus d'espace de pile est nécessaire, l'espace de pile GW-BASIC peut être enregistré et une nouvelle pile configurée pour être utilisée par le sous-programme en langage assembleur. L'espace de pile GW-BASIC doit être restauré, cependant, avant de quitter le sous-programme.

Instruction CALL

CALL variablename[(arguments)]

variablename contient le décalage dans le segment courant du sous-programme appelé.

les arguments sont les variables ou les constantes, séparées par des virgules, qui doivent être passées à la routine.

Pour chaque paramètre dans les arguments, le décalage de 2 octets de l'emplacement du paramètre dans le segment de données (DS) est poussé sur la pile.

Le segment de code d'adresse de retour (CS) GW-BASIC et le décalage (IP) sont poussés sur la pile.

Un long appel à l'adresse de segment donnée dans la dernière instruction DEF SEG et le décalage donné dans variablename transfèrent le contrôle à la routine de l'utilisateur.

Le segment de pile (SS), le segment de données (DS), le segment supplémentaire (ES) et le pointeur de pile (SP) doivent être conservés.

La routine de l'utilisateur a maintenant le contrôle. Les paramètres peuvent être référencés en déplaçant le pointeur de pile (SP) vers le pointeur de base (BP) et en ajoutant un décalage positif à BP.

Lors de l'entrée, les registres de segment DS, ES et SS pointent tous vers l'adresse du segment qui contient le code interpréteur GW-BASIC. Le registre de segment de code CS contient la dernière valeur fournie par DEF SEG. Si aucun DEF SEG n'a été spécifié, il pointe alors vers la même adresse que DS, ES et SS (le DEF SEG par défaut).

Les sept règles suivantes doivent être respectées lors du codage d'un sous-programme :

1. La routine appelée peut détruire le contenu des registres AX, BX, CX, DX, SI, DI et BP. Ils ne nécessitent pas de restauration lors du retour à GW-BASIC. Cependant, tous les registres de segment et le pointeur de pile doivent être restaurés. Les bonnes pratiques de programmation exigent que les interruptions activées ou désactivées soient restaurées à l'état observé lors de l'entrée.

2. Le programme appelé doit connaître le nombre et la longueur des paramètres passés. Les références aux paramètres sont des décalages positifs ajoutés à BP, en supposant que la routine appelée a déplacé le pointeur de pile actuel dans BP ; c'est-à-dire MOV BP,SP. Lorsque 3 paramètres sont passés, l'emplacement de PO est à BP + 10, P1 est à BP + 8 et P2 est à BP + 6.

3. La routine appelée doit faire un RETURN n (n est deux fois le nombre de paramètres dans la liste d'arguments) pour ajuster la pile au début de la séquence d'appel. En outre, les programmes doivent être définis par une instruction PROC FAR.

4. Les valeurs sont renvoyées à GW-BASIC en incluant dans la liste d'arguments le nom de la variable qui reçoit le résultat.

5. Si l'argument est une chaîne, le paramètre offset pointe sur trois octets appelés le descripteur de chaîne. L'octet 0 du descripteur de chaîne contient la longueur de la chaîne (0 à 255). Les octets 1 et 2, respectivement, sont les huit bits inférieurs et supérieurs de l'adresse de début de chaîne dans l'espace de chaîne.

Noter :La routine appelée ne doit modifier le contenu d'aucun des trois octets du descripteur de chaîne.

6. Les chaînes peuvent être modifiées par les routines utilisateur, mais leur longueur ne doit pas être modifiée. GW-BASIC ne peut pas manipuler correctement les chaînes si leurs longueurs sont modifiées par des routines externes.

7. Si l'argument est un littéral de chaîne dans le programme, le descripteur de chaîne pointe vers le texte du programme. Veillez à ne pas modifier ou détruire votre programme de cette façon. Pour éviter des résultats imprévisibles, ajoutez +"" au littéral de chaîne dans le programme.

Par exemple, la ligne suivante force la copie du littéral de chaîne dans l'espace de chaîne alloué en dehors de l'espace mémoire du programme :

20 A$="BASIC"+""

La chaîne peut alors être modifiée sans affecter le programme.

Exemples:

100 DEF SEG=&H2000
110 ACC=&H7FA
120 CALL ACC(A,B$,C)

La ligne 100 définit le segment sur 2000 hex. La valeur de la variable ACC est ajoutée à l'adresse en tant que mot bas après que la valeur DEF SEG est décalée à gauche de quatre bits (c'est une fonction du microprocesseur, pas de GW-BASIC). Ici, ACC est défini sur &H7FA, de sorte que l'appel à ACC exécute le sous-programme à l'emplacement 2000:7FA hex.

Lors de l'entrée, seuls 16 octets (huit mots) restent disponibles dans l'espace de pile alloué. Si le programme appelé nécessite un espace de pile supplémentaire, le programme utilisateur doit réinitialiser le pointeur de pile sur un nouvel espace alloué. Assurez-vous de restaurer le pointeur de pile ajusté au début de la séquence d'appel lors du retour à GW-BASIC.

La séquence suivante en langage assembleur illustre l'accès aux paramètres passés et le stockage d'un résultat de retour dans la variable C.

Noter: Le programme appelé doit connaître le type de variable pour les paramètres numériques passés. Dans ces exemples, l'instruction suivante ne copie que deux octets :

MOVSW

Ceci est adéquat si les variables A et C sont des nombres entiers. Il faudrait copier quatre octets s'ils étaient en simple précision, ou copier huit octets s'ils étaient en double précision.

MOV BP,SP Obtient la position actuelle de la pile dans BP
MOV BX,8[BP] Obtient l'adresse de la description B$
MOV CL,[BX] Obtient la longueur de B$ en CL
MOV DX,1[BX] Obtient l'adresse du descripteur de chaîne B$ dans DX
MOV SI,10[BP] Obtient l'adresse de A en SI
MOV DI,6[BP] Obtient le pointeur vers C dans DI
MOVSW Stocke la variable A dans 'C'
RET 6 Restaure la pile ; Retour

Appels de fonction USR

Bien que l'instruction CALL est la méthode recommandée pour appeler les sous-routines du langage d'assemblage, l'appel de fonction USR est toujours disponible pour la compatibilité avec les programmes écrits précédemment.

Syntaxe:

USR[n](argument)

n est un nombre compris entre 0 et 9 qui spécifie la routine USR appelée (voir instruction DEF USR). Si n est omis, USR0 est supposé.

argument est une expression numérique ou de chaîne.

Dans GW-BASIC, une instruction DEF SEG doit être exécutée avant un appel de fonction USR pour garantir que le segment de code pointe vers la sous-routine appelée. L'adresse de segment donnée dans l'instruction DEF SEG détermine le segment de départ du sous-programme.

Pour chaque appel de fonction USR, une instruction DEF USR correspondante doit avoir été exécutée pour définir le décalage d'appel de fonction USR. Ce décalage et l'adresse DEF SEG actuellement active déterminent l'adresse de début du sous-programme.

Lorsque l'appel de la fonction USR est effectué, le registre AL contient l'indicateur de type de numéro (NTF), qui spécifie le type d'argument donné. La valeur NTF peut être l'une des suivantes :

La valeur NTF spécifie

2 un entier sur deux octets (format complément à deux)
3 une chaîne
4 un nombre à virgule flottante simple précision
8 un nombre à virgule flottante double précision

Si l'argument d'un appel de fonction USR est un nombre (AL<>73), la valeur de l'argument est placée dans l'accumulateur à virgule flottante (FAC).

Le FAC a une longueur de 8 octets et se trouve dans le segment de données GW-BASIC. Le registre BX pointera sur le cinquième octet du FAC.

Si l'argument est un nombre à virgule flottante simple précision :

BX+3 est l'exposant, moins 128. Le point binaire est à gauche du bit le plus significatif de la mantisse.

BX+2 contient les sept bits les plus élevés de la mantisse avec le premier 1 supprimé (sous-entendu). Le bit 7 est le signe du nombre (0=positif, 1=négatif).

BX+1 contient les 8 bits médians de la mantisse.

BX+0 contient les 8 bits les plus bas de la mantisse.

Si l'argument est un entier :

BX+1 contient les huit bits supérieurs de l'argument.

BX+0 contient les huit bits inférieurs de l'argument.

Si l'argument est un nombre à virgule flottante double précision :

BX+0 à BX+3 sont les mêmes que pour la virgule flottante simple précision.

BX-1 à BX-4 contiennent quatre octets supplémentaires de mantisse. BX-4 contient les huit bits les plus bas de la mantisse.

Si l'argument est une chaîne (indiquée par la valeur 3 stockée dans le registre AL), la paire de registres (DX) pointe sur trois octets appelés le descripteur de chaîne. L'octet 0 du descripteur de chaîne contient la longueur de la chaîne (0 à 255).

Les octets 1 et 2, respectivement, sont les huit bits inférieurs et supérieurs de l'adresse de début de chaîne dans le segment de données GW-BASIC.

Si l'argument est un littéral de chaîne dans le programme, le descripteur de chaîne pointe vers le texte du programme. Veillez à ne pas modifier ou détruire des programmes de cette manière (voir l'instruction CALL précédente).

Généralement, la valeur renvoyée par un appel de fonction USR est du même type (entier, chaîne, simple précision ou double précision) que l'argument qui lui a été transmis. Les registres qui doivent être conservés sont les mêmes que dans l'instruction CALL.

Un retour lointain est nécessaire pour quitter le sous-programme USR. La valeur retournée doit être stockée dans le FAC.

Programmes qui appellent des programmes en langage d'assembleur

Cette section contient deux exemples de programmes GW-BASIC

qui charge une routine en langage assembleur pour additionner deux nombres,

retourne la somme en mémoire

reste résident en mémoire.

Le segment de code et le décalage vers la première routine sont stockés dans le vecteur d'interruption à 0:100H.

L'exemple 1 appelle une sous-routine en langage assembleur :

10 DEF SEG=0
100 CS=PEEK(&H102)+PEEK(&H103)*256
200 OFFSET=PEEK(&H100)+PEEK(&H101)*256
250 DEF SEG
300 C1%=2:C2%=3:C3%=0
400 TWOSUM=OFFSET
500 DEF SEG=CS
600 CALL TWOSUM(C1%,C2%,C3%)
700 PRINT C3%
800 END

La sous-routine en langage assembleur appelée dans le programme ci-dessus doit être assemblée, liée et convertie en un fichier .COM.

Le programme, lorsqu'il est exécuté avant l'exécution du programme GW-BASIC, restera en mémoire jusqu'à ce que l'alimentation du système soit coupée ou que le système soit redémarré.

0100            org 100H
0100            double segment
                assume cs:double
0100 EB 17 90   start: jmp start1
0103            usrprg proc far
0103 55         push bp
0104 8B EC      mov bp,sp
0106 8B 76 08   mov si,[bp]+8        ;get address of parameter b
0109 8B 04      mov ax,[si]          ;get value of b
010B 8B 76 0A   mov si,[bp]+10       ;get address of parameter a
010E 03 04      add ax,[si]          ;add value of a to value of b
0110 8B 7E 06   mov di,[bp]+6        ;get address of parameter c
0113 89 05      mov di,ax            ;store sum in parameter c
0115 5D         pop bp
0116 ca 00 06   ret 6
0119            usrprg endp
                                     ;Program to put procedure in
                                     ;memory and remain resident. The
                                     ;offset and segment are stored
                                     ;in location 100-103H.
0119            start1:
0119 B8 00 00   mov ax,0
011C 8E D8      mov ds,ax            ;data segment to 0000H
011E BB 01 00   mov bx,0100H         ;pointer to int vector 100H
0121 83 7F 02 0 cmp word ptr [bx],0
0125 75 16      jne quit             ;program already run, exit
0127 83 3F 00   cmp word ptr2 [bx],0
012A 75 11      jne quit             ;program already run exit
012C B8 01 03 R mov ax,offset usrprg
012F 89 07      mov [bx],ax          ;program offset
0131 8C c8      mov ax,cs
0133 89 47 02   mov [bx+2],ax        ;data segment
0136 0E         push cs
0137 1F         pop ds
0138 BA 0141 R  mov dx,offset veryend
013B CD 27      int 27h
013D            quit:
013D CD 20      int 20h
013F            veryend:
013F            double ends
                end start

L'exemple 2 place la sous-routine du langage assembleur dans la zone spécifiée :

10 I=0:JC=0
100 DIM A%(23)
150 MEM%=VARPTR(A%(1))
200 FOR I=1 TO 23
300 READ JC
400 POKE MEM%,JC
450 MEM%=MEM%+1
500 NEXT
600 C1%=2:C2%=3:C3%=0
700 TWOSUM=VARPTR(A%(1))
800 CALL TWOSUM(C1%,C2%,C3%)
900 PRINT C3%
950 END
1000 DATA &H55,&H8b,&Hec &H8b,&H76,&H08,&H8b,&H04,&H8b,&H76
1100 DATA &H0a,&H03,&H04,&H8b,&H7e,&H06,&H89,&H05,&H5d
1200 DATA &Hca,&H06,&H00

 

 

 

 

 

 

 

Recherche personnalisée