L’assembeur en QBasic
Par Aaron Severn (22 décembre 1997)
Traduction par : Asher256 (Octobre 2002)
Introduction
Il est toujours avantageux d’intégrer des routines en langage assembleur dans vos programmes QuickBasic afin d’en optimiser la vitesse d’exécution.
Cela permet également d’appeler directement les interruptions du BIOS ou du DOS, ce qui reste irréalisable en QuickBasic natif.
Ce document n’a pas pour but de vous enseigner l’assembleur (il existe de nombreux tutoriels dédiés à ce sujet), mais plutôt de vous expliquer comment interfacer vos routines de bas niveau avec QuickBasic.
De nombreux développeurs maîtrisent le langage assembleur sans pour autant savoir comment obtenir les opcodes indispensables à l’utilisation de l’instruction CALL ABSOLUTE. Ce tutoriel est conçu pour combler cette lacune.
Utilisation de DEBUG pour générer du code machine
Dans le répertoire des commandes externes de MS-DOS (ou sous le répertoire WINDOWS\COMMAND sous Windows 95/98), vous trouverez un utilitaire nommé DEBUG.EXE.
Lorsque vous l’exécutez, le programme affiche une invite de commande sous la forme d’un simple tiret :
-
Saisissez la commande ‘a’ (pour Assemble) puis appuyez sur Entrée. L’utilitaire affiche alors une adresse mémoire de départ, par exemple :
-a
199D:0100
Vous pouvez dès lors commencer à saisir vos instructions en assembleur. Prenons un cas pratique simple : l’initialisation et l’affichage du curseur de la souris via l’interruption 33h.
Le code assembleur correspondant est le suivant :
PUSH ax
XOR ax,ax
INT 33
INT 33
MOV ax,1
INT 33
POP ax
retf
Saisissez ces instructions ligne par ligne dans DEBUG.
À la fin de votre saisie, validez par une ligne vide (en appuyant directement sur Entrée). Cela indique au programme que vous avez terminé, et vous reviendrez à l’invite de commande principale (le tiret).
Saisissez maintenant la commande ‘u’ (pour Unassemble) afin de désassembler le bloc mémoire. Le code s’affiche alors à l’écran, structuré en plusieurs colonnes.
La première colonne indique l’adresse mémoire de chaque instruction, la deuxième affiche les opcodes en code machine, tandis que les colonnes suivantes d’un point de vue textuel détaillent les instructions assembleur.
La colonne qui nous intéresse pour notre programme QuickBasic est la deuxième. Vous devriez obtenir la suite d’opcodes suivante :
50
31C0
CD33
B80100
CD33
58
CB
Ces valeurs étant exprimées en hexadécimal, il conviendra de leur adjoindre le préfixe &H lors de leur intégration dans votre code QuickBasic.
Une fois les codes notés, vous pouvez quitter l’utilitaire DEBUG en saisissant la commande ‘q’ (pour Quit).
Intégration du code machine dans QuickBasic
Pour exécuter ce code, nous allons faire appel à l’instruction intégrée CALL ABSOLUTE. Les opcodes machine précedemment obtenus doivent être concaténés au sein d’une chaîne de caractères en utilisant la fonction CHR$.
Votre script QuickBasic se structurera de la manière suivante :
DIM asm as STRING
ASM = ASM + CHR$(&H50)
ASM = ASM + CHR$(&H31) + CHR$(&HC0)
ASM = ASM + CHR$(&HCD) + CHR$(&H33)
ASM = ASM + CHR$(&HB8) + CHR$(&H1) + CHR$(&H0)
ASM = ASM + CHR$(&HCD) + CHR$(&H33)
ASM = ASM + CHR$(&H58)
ASM = ASM + CHR$(&HCB)
Il ne reste plus qu’à l’exécuter. Pour ce faire, modifiez le segment par défaut de QuickBasic afin de cibler l’adresse mémorielle de la chaîne de caractères, puis invoquez l’instruction CALL ABSOLUTE en lui transmettant l’offset (adresse relative) obtenu via la fonction SADD.
Ajoutez ces lignes de code pour exécuter le programme de bas niveau :
DEF SEG = VARSEG(ASM)
CALL ABSOLUTE (SADD(ASM))
DEF SEG
Vous disposez désormais de la méthode fondamentale pour lier l’assembleur à QuickBasic. Cependant, un mécanisme indispensable reste à étudier : le passage d’arguments.
Si vous développez une routine plus complexe, telle qu’un tracé de point optimisé, vous devez lui transmettre des variables (les coordonnées X, Y et la couleur). Lorsque vous ajoutez des paramètres à l’instruction CALL ABSOLUTE, QuickBasic les empile de manière séquentielle dans la pile d’exécution (STACK). Pour y accéder depuis votre code assembleur, vous devez initialiser un pointeur de pile stable à l’aide du registre BP, selon la structure suivante :
PUSH bp
MOV bp,sp
.
. [votre code assembleur ici]
.
POP bp
Le registre BP pointe à présent sur le sommet de la pile. Prenons un exemple où l’appel QuickBasic transmettrait trois arguments :
DEF SEG = VARSEG(ASM)
CALL ABSOLUTE (x, y, colour, SADD(ASM))
Dans cette configuration, l’organisation de la pile (STACK) se présente de la manière suivante :
0A x
08 y
06 colour
04 QBasic retour de l'offset
02 QBasic retour du segment
00 bp (est mis sur le STACK par le code ASM)
Pour lire ou modifier la valeur de x, il vous suffira donc d’accéder à l’adresse relative [bp + 0Ah]. Attention : toute utilisation ultérieure de l’instruction PUSH au sein de votre routine modifiera l’état de la pile.
Par exemple, si vous effectuez un PUSH ax, l’argument x se situera alors à l’adresse [bp + 0Ch]. Un PUSH bx supplémentaire le décalera à l’adresse [bp + 0Eh], et ainsi de suite.

