1999. Alors que la fin du millénaire approche à grand pas, je traîne mes guêtres à l'IUT informatique de Nantes. Durant un TP, nous avons la chance de découvrir l'assembleur et ses nombreuses possibilités. Avec un ami, Sébastien Goude, nous prenons la folle décision d'écrire notre propre "O.S." Il s'appellera S.O.S. : a Simple Operating System. Nous avons réussi à créer notre propre disquette de boot, entièrement autonome et indépendante de tout système d'exploitation.
En 1999 également, je lance un site internet : la Caverne Informatique. L'article S.O.S. : A Simple Operating System est l'un des premiers à y figurer.
Lu quelques années plus tard, ce tutorial peut sembler obsolète. Aujourd'hui les lecteurs de disquettes et de CD-Rom sont en voie de disparition, remplacés lentement mais sûrement par les clés USB et les graveurs DVD.
Mais il ne faut pas oublier que nos PC tournent en suivant des normes, des protocoles, des spécifications qui datent de 25 ans et plus. Obsolète, cet article ? ;-)
Le projet S.O.S. a été entièrement écrit en assembleur 16-bits, cet article suppose que vous connaissez ce langage. Si ce n'est pas le cas, consultez auparavant cette courte introduction.
Qu'appelle-t-on zone de boot ? Lorsque l'ordinateur est mis sous tension, il se met à la recherche d'un programme boot. Il s'agit d'un programme de petite taille capable de lancer le système d'exploitation. Pour des raisons d'efficacité, le programme boot se situe toujours au même endroit sur le disque dur.
Disquettes et disques durs sont organisés sur le même principe. Chaque disque magnétique est divisé en pistes (organisées en cercles concentriques), et chacune de ces pistes se divise elle-même en plusieurs secteurs.
Le premier secteur d'une disquette ou d'un disque dur est toujours réservé au programme de boot. A titre d'exemple, le secteur boot de Dos/Windows a la structure suivante :
+------------------------+
0x000 | Saut au programme |
|------------------------|
0x003 | |
| Paramètres disque |
| |
|------------------------|
0x02C/0x03E | |
| Code du Programme |
| |
| |
|------------------------|
0x1FE | Nombre Magique (0xAA55)|
+------------------------+
Linux utilise une structure très semblable, mais ne contient pas la partie "Paramètres disques". Cette partie est utilisée par DOS pour connaître les caractéristiques de la disquette (nombre de secteurs par piste, etc...). Le nombre magique permet à DOS/Windows de vérifier qu'il s'agit bien d'une disquette formatée DOS.
Bien entendu, nous sommes libre d'organiser notre secteur comme bon nous semble. Notre disquette boot ne contient que la partie code. En effet, la partie "paramètres disque" n'est pas utile vu l'avancement de notre projet, le nombre magique non plus. Quand au saut, il est également inutile car le programme est adjacent.
[haut de page]Voici le principe que nous suivrons.
; Code qui sera exécuté sous DOS Définir Buffer de 512 octets (taille d'un secteur disquette) Charger dans Buffer le code situé après le label 'A_Copier:' Ecrire Buffer sur le secteur Boot de la disquette (piste 0 secteur 1) Quitter et revenir à MS-DOS ; Code qui sera compilé sous DOS mais pas exécuté sous DOS Label A_Copier: Afficher caractère
Après exécution de la dernière ligne du programme, le processeur plantera car il essaiera d'exécuter l'instruction suivante, qui peut être n'importe quoi. Si vous avez initialisé votre buffer, ce sera 0x00, une instruction inconnue.
Faire planter le processeur ne l'endommage pas physiquement. Heureusement, car vue la fréquence à laquelle Windows plante, nos PC ne dureraient pas très longtemps. La seule conséquence est qu'il faudra redémarrer l'ordinateur.
Voilà la théorie. Dans la pratique, durant notre développement, nous avons rencontré plusieurs soucis :
Le code source du premier programme boot est le suivant. Copiez-collez-le dans un fichier nommé sos.asm, puis compilez-le en tapant tasm sos puis tlink /t sos. Tapez alors sos pour exécuter le programme.
[haut de page]Exécuter un programme depuis la disquette est à la fois lent et limité. Le programme doit donc être capable de se charger lui-même en mémoire. Nous rajoutons donc un morceau de code qui va réaliser une copie en mémoire du contenu du secteur boot.
; Code qui sera exécuté sous DOS Définir Buffer de 512 octets (taille d'un secteur disquette) Charger dans Buffer le code situé après le label 'Chargeur:' Compter Nombre d'octets copiés Ecrire Buffer sur le secteur Boot de la disquette (piste 0 secteur 1) Charger dans Buffer le code situé après le label 'Programme:' Compter Nombre d'octets copiés Ecrire Buffer sur piste 0 secteur 2 Quitter et revenir à MS-DOS ; Code qui sera compilé sous DOS mais exécuté uniquement lors du boot Label Chargeur: Charger à l'adresse mémoire 1000h le secteur 2 de la disquette (choix arbitraire) Sauter à l'adresse mémoire 1000h Label Programme: Afficher un caractère
Le défaut de ce code est d'occuper 2 secteurs de la disquette (= 1024 octets). Or avec le compteur, on se rend compte qu'un tel code ne dépasse pas 100 octets. Il y a moyen d'optimiser, et il faut le faire, parce que si on commence dès maintenant à perdre de la place, il va vite ne plus rester grand chose.
[haut de page]; Code qui sera exécuté sous DOS Définir Buffer de 512 octets (taille d'un secteur disquette) Charger dans Buffer le code situé après le label 'Chargeur:' Compter Nombre d'octets copiés Ecrire Buffer sur le secteur Boot de la disquette (piste 0 secteur 1) Quitter et revenir à MS-DOS ; Code qui sera compilé sous DOS mais exécuté uniquement lors du boot Label Chargeur: Comparer Cs (Code Segment) avec 1000h Si Cs = 1000h alors sauter au label 'Programme' Sinon Charger à l'adresse mémoire 1000h le secteur 1 de la disquette (choix arbitraire) Sauter à l'adresse mémoire 1000h Label Programme: afficher caractère[haut de page]
Vous l'aurez deviné, écrire un système d'exploitation complètement en assembleur est
Il y a possibilité de remplacer avantageusement l'assembleur par du C. En effet, le C est considéré par certains comme une sorte de super-assembleur, grâce à l'utilisation aisée des pointeurs. Voici un exemple de code assembleur qui affiche 'Salut' :
Mov Ax, 0B800h ; Adresse de la carte vidéo Mov Es, Ax Mov Bx, 0 Mov Es:[Bx], 'S' Mov Es:[Bx+2], 'A' ; On va de deux en deux Mov Es:[Bx+4], 'L' ; car un octet sur deux Mov Es:[Bx+6], 'U' ; sert à l'attribut Mov Es:[Bx+8], 'T'
Eh bien, le même résultat sera obtenu en C avec le code suivant :
unsigned char *pointeur = (unsigned char *) 0x0B800 pointeur[0] = 'S'; pointeur[2] = 'A'; pointeur[4] = 'L'; pointeur[6] = 'U'; pointeur[8] = 'T';
De même qu'en assembleur vous ne pouviez pas utiliser l'interruption 21h, en C vous ne pourrez pas utiliser les librairies telles que stdio.h. Elles sont dépendantes du système d'exploitation, elle font appel à des fonctions propres à DOS/Windows, ou Linux, ou l'O.S. sous lequel vous compilez). Pour produire du code 'compatible', vous ne devez faire appel qu'à ce que vous aurez de disponible au moment du boot, c'est-à-dire rien.
[haut de page]On appelle "interruptions" des programmes (ou routines) qui s'exécutent lorsque survient un événement. En général, elles sont appelées par un programme. D'autres interruptions sont déclenchées par le matériel (lorsqu'une touche est tapée au clavier par exemple, une routine se charge de l'identifier et de placer le code ASCII correspondant dans le buffer).
On différencie interruptions matérielles (IRQ) et logicielles. On différencie les interruptions MS-DOS des interruptions BIOS. Les interruptions MS-DOS ne sont évidemment pas disponibles lorsque le DOS ou Windows n'a pas été lancé. D'où la nécessité de développer nos interruptions, notre API, nos fonctions.
La table des vecteurs d'interruption fournit, pour chaque interruption, l'adresse de la routine correspondante. Elle se situe au début de la mémoire vive (segment 0). L'offset est donné par le numéro d'interruption.
[haut de page]DOS, et son ancêtre CP/M sont des systèmes d'exploitation en mode réel. Cela signifie que ces systèmes sont incapables de contrôler les programmes qu'ils exécutent. Entendez par là que n'importe quel programme peut prendre le contrôle de l'ordinateur, accéder à toute la mémoire, dialoguer avec tous les périphériques, sans rien demander au système d'exploitation. En mode réel, un programme une fois lancé, est seul à s'exécuter - le multitâche est impossible. Donc si ce programme plante, il fait planter tout l'ordinateur.
Windows et Linux sont des systèmes d'exploitation en mode protégé. Il s'agit d'un mode spécial du processeur qui permet - entre autres - de protéger certaines zones de la mémoire grâce à un système de privilèges. Ainsi, le système d'exploitation dispose du niveau de privilège le plus élevé - il a le contrôle total de l'ordinateur - tandis que les programmes utilisateur ont un niveau de privilège le plus bas - ils sont contrôlés par le système d'exploitation :
Lorsque le programme tente d'exécuter une instruction interdite (le plus souvent, un accès non autorisé à la mémoire), le système d'exploitation le tue : il arrête de l'exécuter, et affiche un message d'erreur :
Malgré le coup de vieux qu'a pris cet article, il reste toujours d'actualité. Découvrant la programmation, découvrant l'assembleur, nous étions assez fier du résultat obtenu à l'époque.
Depuis, nous avons un peu délaissé le projet S.O.S. pour nous consacrer à d'autres sujets tout aussi passionnants.
Cependant, gardez un oeil dessus ; un de ces jours, il se pourrait que de nouveaux articles viennent compléter celui-ci et prendre la suite du projet S.O.S.
[Haut de page]Si vous avez apprécié (ou détesté) cet article ou ce site, vous avez la possibilité de donner une note d'évaluation et indiquer les points forts, les points faibles de ce site. Ce service est offert par l'institut de statistiques Weborama.
Eric Minso - novembre 2003 - La Caverne Informatique - http://cavinfo.fr.st/