Copyright © 2002 Krishnakumar R.
Copyright © 2011 Sébastien Marbrier
Article paru dans le n°079 de la Gazette Linux de juin 2002.
Cet article est publié selon les termes de la Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.
Table des matières
La première partie a été publiée en avril.
Après avoir appris comment créer le secteur d'amorçage, il faut comprendre le fonctionnement des interruptions du BIOS avant d'étudier le basculement sur le mode protégé. Le BIOS fournit des routines bas niveau que l'on appelle interruptions BIOS. Ces routines facilitent le travail du concepteur du système d'exploitation. C'est l'objet de cet article.
Le BIOS copie le secteur d'amorçage dans la mémoire et y exécute le code. En outre, le BIOS prend en charge bien d'autres choses. Quand un système d'exploitation démarre, il n'a pas de pilote pour la vidéo, les disques ou tout autre chose. Il est presque impossible d'inclure de tels pilote dans le secteur d'amorçage. Il faut donc trouver une autre solution. C'est ici que le BIOS nous vient en aide en nous proposant de nombreuses routines. Par exemple, il y a des routines prêtes à l'emploi pour toutes sortes d'usages tels que vérifier les équipements installés, contrôler l'imprimante, déterminer la taille de la mémoire, etc. Ce sont ces routines que nous appelons interruptions BIOS.
Dans les langages de programmation habituels, les routines sont invoquées au
moyen d'un appel à la routine. Par exemple, dans un programme en C, s'il y a
une routine dont le nom est display
, ayant pour paramètres
noofchar
– le nombre de caractères à afficher,
attr
– l'attribut des caractères affichés il
suffit d'appeler la routine. Ici nous utilisons les interruptions. Nous
utilisons l'instruction assembleur int
.
Par exemple pour écrire quelque chose à l'écran nous appelons la fonction C de cette façon :
display(noofchar, attr);
Nous écrivons l'équivalent avec le BIOS :
int 0x10
Avant d'appeler l'interruption BIOS, nous avons besoin de charger certaines valeur dans un format particulier dans les registres. Imaginons que nous utilisons l'interruption BIOS 13h qui permet de transférer les données de la disquette vers la mémoire. Avant d'appeler l'interruption 13h nous devons spécifier le segment d'adresse sur lequel les données seront copiées. Nous avons également besoin de passer en paramètre le numéros du lecteur, de piste, de secteur, le nombre de secteurs à transférer, etc. Ce que nous faisons en chargeant les registres avec les valeurs nécessaires. Vous aurez les idées claires après avoir lu les explications sur le secteur d'amorçage que nous allons créer.
Le point important est qu'une même interruption peut être utilisée pour toutes sortes d'usages. L'usage que l'on fait d'une interruption donnée dépend du numéro de la fonction utilisée. Le choix de la fonction est fait en fonction de la valeur du registre ah. Par exemple, l'interruption 13h peut servir à afficher une chaîne ou bien à récupérer les coordonnées du curseur. Si on place la valeur 3 dans le registre ah, alors la fonction numéro 3 qui permet de récupérer la position du curseur est sélectionnée. Pour afficher une chaîne, on place 13h dans le registre ah.
Cette fois notre code source est composé de deux programmes en assembleur et d'un programme en C. Le premier fichier assembleur est le code du secteur de démarrage. Nous avons écrit dans le secteur d'amorçage le code pour copier le deuxième secteur de la disquette dans le segment mémoire 0x500 (la position de l'adresse est 0x5000). Ce que nous faisons en utilisant l'interruption BIOS 13h. Le code dans le secteur d'amorçage passe ensuite la main à l'offset 0 du segment 0x500. Le code du deuxième fichier assembleur permet d'afficher un message à l'écran au moyen de l'interruption BIOS 10h. Le programme en C sert à transférer le code exécutable issu du premier fichier assembleur sur le secteur d'amorçage et le code exécutable généré par le second fichier assembleur dans le deuxième secteur de la disquette.
Grâce à l'interruption 13h, le secteur de démarrage charge le deuxième secteur de la
disquette à l'adresse mémoire 0x5000 (segment d'adresse 0x500). Le code source
ci-dessous remplit ce rôle. Sauvegardez-le dans le fichier bsect.s
LOC1=0x500 entry start start: mov ax,#LOC1 mov es,ax mov bx,#0 mov dl,#0 mov dh,#0 mov ch,#0 mov cl,#2 mov al,#1 mov ah,#2 int 0x13 jmpi 0,#LOC1
La première ligne est similaire à une macro. Les deux instructions suivantes vous sont peut-être familières. Nous chargeons ensuite la valeur 0x500 dans le registre es, qui est l'adresse à laquelle le code du deuxième secteur de la disquette (le premier secteur étant le secteur de démarrage) est copié. Nous plaçons le décalage du segment à zéro.
Ensuite on charge le numéro du disque dans le registre dl, le numéro de la tête dans le registre dh, le numéro de la piste dans le registre ch, le numéro de secteur dans le registre cl et le nombre de secteurs à transférer dans le registre al. Donc on va charger le secteur 2 de la piste 0 du disque 0 dans le segment 0x500. Tout ceci correspond à une disquette 1,44Mo
Le chargement de la valeur 2 dans le registre ah permet la sélection de la fonction numéro 2 qui permet de copier les données depuis une disquette.
On appelle à présent l'interruption 13h et enfin, on saute au déplacement 0 du segment 0x500.
Le code ci-dessous est utilisé pour le second secteur :
entry start start: mov ah,#0x03 xor bh,bh int 0x10 mov cx,#26 mov bx,#0x0007 mov bp,#mymsg mov ax,#0x1301 int 0x10 loop1: jmp loop1 mymsg: .byte 13,10 .ascii "Handling BIOS interrupts"
Ce code sera chargé dans le segment 0x500 et exécuté. Ce code utilise l'interruption 10h pour récupérer la position du curseur et afficher un message.
Les trois premières lignes de code (commençant à la 3ème ligne) servent à récupérer la position courante du curseur. Ici, la fonction numéro 3 de l'interruption 13h est sélectionnée. Ensuite on efface la valeur du registre bh. On charge le nombre de caractères de la chaîne dans le registre ch. Dans bx on charge le numéro de page et l'attribut qui doit être utilisé lors de l'affichage. Dans ce cas, on prévoit d'afficher des caractères blancs sur un fond noir. Ensuite l'adresse du message à afficher et placée dans le registre bp. Le message est composé de deux octets ayant pour valeurs respectives 13 et 10 qui correspondent à l'ensemble retour chariot (CR) et fin de ligne (LF). Il y a ensuite une chaîne de 24 caractères. Après on sélectionne la fonction qui correspond à l'affichage de la chaîne et au déplacement du curseur. Ensuite vient l'appel de l'interruption. Et finalement vient la boucle habituelle.
Le code source du programme en C est présenté ci-dessous. Enregistrez-le sous le nom
write.c
#include <sys/types.h> /* unistd.h needs this */ #include <unistd.h> /* contains read/write */ #include <fcntl.h> int main() { char boot_buf[512]; int floppy_desc, file_desc; file_desc = open("./bsect", O_RDONLY); read(file_desc, boot_buf, 510); close(file_desc); boot_buf[510] = 0x55; boot_buf[511] = 0xaa; floppy_desc = open("/dev/fd0", O_RDWR); lseek(floppy_desc, 0, SEEK_SET); write(floppy_desc, boot_buf, 512); file_desc = open("./sect2", O_RDONLY); read(file_desc, boot_buf, 512); close(file_desc); lseek(floppy_desc, 512, SEEK_SET); write(floppy_desc, boot_buf, 512); close(floppy_desc); }
J'ai décrit dans la première partie de cet article la création de la disquette
de démarrage. Il y a quelques légères différences. Tout d'abord, on copie sur
le secteur d'amorçage le fichier bsect
, le fichier
exécutable obtenu à partir de bsect.s
. Ensuite on copie
l'exécutable sect2
qui vient de
sect2.s
dans le second secteur de la disquette. Ainsi les
modifications à réaliser pour rendre la disquette amorçable ont été réalisées.
On peut télécharger les sources depuis
Retirez l'extension .txt
des fichiers et sur l'invite de la console
tapez :
make
vous pouvez également tout compiler séparément ; tapez :
as86 bsect.s -o bsect.o ld86 -d bsect.o -o bsect
et répétez l'opération avec sect2.s
pour obtenir sect2
. Compilez write.c
et exécutez-le après l'insertion de la disquette dans le lecteur en tapant :
cc write.ci -o write ./write
Après le démarrage au moyen de la disquette, on peut voir l'affichage de la chaîne de caractères. Jusqu'à présent nous avons utilisé les interruptions du BIOS. Dans la prochaine partie de cette série je souhaite expliquer comment basculer le processeur sur le mode protégé. D'ici là, au revoir !
L'adaptation française de ce document a été réalisée dans le cadre du Projet de traduction de la Gazette Linux.
Vous pourrez lire d'autres articles traduits et en apprendre plus sur ce projet en visitant notre site : http://www.traduc.org/Gazette_Linux.
Si vous souhaitez apporter votre contribution, n'hésitez pas à nous rejoindre, nous serons heureux de vous accueillir.