Écrivez votre mini OS (Partie 2)

Gazette Linux n°079 — Juin 2002

Sébastien Marbrier

Adaptation française  

Frédéric Marchal

Correction du DocBook 

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

1. La théorie
1.1 Pourquoi le BIOS ?
1.2 Comment invoquer les interruptions du BIOS ?
1.3 Et maintenant, comment passe t-on les paramètres ?
2. Que faisons-nous ?
3. Le secteur d'amorçage
4. Le second secteur
5. Le programme en C
6. Téléchargements
7. Et ensuite ?
À propos de l'auteur

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.

1. La théorie

1.1 Pourquoi le BIOS ?

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.

1.2 Comment invoquer les interruptions du 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

1.3 Et maintenant, comment passe t-on les paramètres ?

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.

2. Que faisons-nous ?

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.

3. Le secteur d'amorçage

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.

4. Le second secteur

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.

5. Le programme en C

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.

6. Téléchargements

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

7. Et ensuite ?

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 !

À propos de l'auteur

Krishnakumar R.

Krishnakumar est un étudiant en dernière année à l'Université publique Thissur Kerala, en Inde. Son voyage au pays des Systèmes d'exploitation a commencé avec la progammation de modules linux. Il a construit un système d'exploitation de routage nommé GROS (Les détails sont disponibles sur sa page personnelle : http://www.askus.way.to/). Ses autres centres d'intérêts incluent le réseau, les pilotes de périphériques, le portage et les systèmes embarqués.

Adaptation française de la Gazette Linux

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.