Notre BIOS a comme première fonction de prendre en charge une partie de la gestion du matériel et de fournir quelques abstractions matérielles minimales au système d'exploitation et à l'utilisateur. Pour simplifier le travail, on a incorporé de petits programmes dans notre BIOS dont le rôle était de gérer une partie du matériel présent sur la carte mère. Ces programmes ont été standardisés de façon à assurer la compatibilité des programmes utilisant ces routines sur tous les BIOS existants. Ce n'est pas pour rien que « BIOS » est l'abréviation de Basic Input Output Software, ce qui signifie « programme basique d'entrée − sortie ».
Rappel sur les interruptions
Pour communiquer avec le matériel, le BIOS utilise des fonctionnalités de notre processeur qu'on appelle les interruptions.
Oh là, c'est quoi, une interruption ?
C'est une fonctionnalité de notre processeur qui permet d'arrêter temporairement l'exécution d'un programme pour en exécuter un autre. On utilise les interruptions dans quelques cas bien précis, qui nécessitent un traitement ne pouvant attendre trop longtemps : communiquer avec un ou des périphériques, par exemple. Ainsi, pour communiquer avec une carte graphique, un disque dur ou un clavier, notre BIOS devra utiliser des interruptions.
Celles-ci ont pour but d'interrompre l'exécution d'un programme afin de réagir à un événement extérieur (matériel, erreur fatale d'exécution d'un programme…) et de le traiter en temps voulu, avant de rendre la main au programme interrompu. Notre interruption va donc effectuer un petit traitement (ici, communiquer avec un périphérique). Ce traitement est effectué par un petit programme auquel on a donné un nom technique : routine d'interruption.
Lorsqu'un processeur doit exécuter une interruption, celui-ci :
arrête l'exécution du programme en cours ;
exécute la routine d'interruption ;
reprend l'exécution du programme suspendu là où elle en était.
Il existe trois moyens pour déclencher une interruption :
une instruction un peu spéciale du processeur ;
les exceptions, qui se produisent automatiquement lorsque le processeur rencontre une erreur (par exemple une division par zéro) ;
les requêtes d'interruption, qui sont déclenchées par un événement d'origine matérielle.
Comme vous le voyez, les interruptions peuvent non seulement être appelées par un programme quelconque, grâce à l'instruction int
, mais elles permettent aussi de réagir à des événements purement matériels, comme l'appui d'une touche au clavier.
Comme on l'a dit, une interruption a été conçue pour réagir à un événement, mais c'est avant tout un programme, qui peut être exécuté comme n'importe quel autre programme. Dans notre cas, ces interruptions seront simplement considérées comme des programmes simplistes permettant d'agir sur un périphérique. Bien sûr, devant la multiplicité des périphériques, on se doute bien qu'il n'existe pas d'interruption à tout faire : il va de soi qu'un programme envoyant un ordre au disque dur sera différent d'un programme agissant sur une carte graphique. Dans chaque cas, on aura besoin d'effectuer un traitement différent, on doit donc disposer de plusieurs routines d'interruption. Pour le cas de notre BIOS, celui-ci fournit beaucoup de routines de base pour communiquer avec les périphériques de notre ordinateur.
Vecteur d'interruption
Cependant, il faut bien décider quelle est l'interruption à exécuter suivant la situation. Par exemple, exécuter l'interruption de gestion du clavier alors qu'on souhaite communiquer avec notre disque dur produirait un résultat plutôt comique. :p Par conséquent, on doit stocker plusieurs de ces routines dans sa mémoire. Mais comment les retrouver ? Comme les autres données ! Pour expliquer correctement cela, il va falloir faire un petit rappel.
La mémoire de notre ordinateur est un vulgaire amas de cellules mémoire, chacune capable de retenir des bits ; on rappelle qu'un bit est une information qui peut prendre deux valeurs : 0 ou 1. Ces cellules sont regroupées en paquets de 8, qu'on appelle des cases mémoire. Ces cases contiennent donc des données (ou des morceaux de données : un nombre entier occupe souvent plusieurs cases). Pour savoir dans quelle case mémoire on a stocké notre donnée, notre ordinateur identifie chaque case par un nombre unique : l'adresse mémoire. Chaque routine est alors placée dans la mémoire à un certain endroit, localisable par son adresse, qui indique sa position dans la mémoire.
Pour retrouver la position de notre routine et savoir laquelle exécuter, certains ordinateurs utilisent une partie de leur mémoire pour stocker les adresses de début de toutes les routines d'interruption. En gros, cette partie de la mémoire contient les adresses permettant de localiser chaque routine. Cette portion de la mémoire s'appelle le vecteur d'interruption. Pour chaque interruption, une partie fixe de la mémoire contient l'adresse de début de l'interruption à effectuer.
Le BIOS initialise le vecteur d'interruption au démarrage de l'ordinateur et fournit quelques interruptions de gestion du matériel fondamentales. Néanmoins, le système d'exploitation peut fournir ses propres routines. Pour que celles-ci soient exécutées, il suffit à l'OS de détourner l'interruption. Cela consiste à remplacer l'adresse de l'interruption contenue dans le vecteur d'interruption par l'adresse de la routine que l'OS a chargée en mémoire (bien sûr, l'ancienne adresse est sauvegardée).
En clair, le vecteur d'interruption ne contiendra plus l'adresse servant à localiser la routine du BIOS, mais celle localisant la routine de l'OS.
Routines d'interruption du BIOS
Le BIOS fournit de base un grand nombre de routines d'interruption préprogrammées. Ces routines peuvent, par exemple, être exécutées quand on appuie sur une touche du clavier, ou quand une information en provenance de la souris est disponible sur le port PS/2.
De nos jours (depuis Windows 95), une fois le système d'exploitation chargé, ces routines ne peuvent être utilisées que par les noyaux des systèmes d'exploitation, et quelques pilotes. Pour ceux qui ne le savent pas, une fois le système d'exploitation chargé, la mémoire d'un ordinateur x86 (architecture 32 bits, ce qui signifie que nous pouvons charger 32 bits de données en même temps) est coupée en au moins deux portions, dont deux principales :
une partie dans laquelle tout programme qui s'y trouve peut gérer les périphériques et effectuer des manipulations avancées sur la mémoire : l'espace noyau ;
une autre pour les programmes qui ne doivent ni manipuler les périphériques ni faire de manipulations avancées sur la mémoire : l'espace utilisateur.
Nos routines sont alors inaccessibles par un programme localisé dans l'espace utilisateur. Uniquement les programmes localisés dans l'espace noyau ont le droit d'accéder au matériel. Seule une partie du système d'exploitation, nommée noyau et contenant quelques drivers, est placée dans l'espace noyau et peut donc faire exécuter les routines du BIOS si besoin est.
Mais attention : cela ne vaut qu'une fois que le système d'exploitation est chargé (en fait, une fois que le processeur passe du mode réel où tout est permis au mode protégé où certaines opérations sont restreintes). Avant, la mémoire est gérée autrement, et il n'existe pas de distinction entre espace noyau et espace utilisateur, ce qui permet au BIOS de s'exécuter librement.
Les routines du BIOS, voire l'intégralité de son contenu, étaient parfois recopiées dans la mémoire RAM afin de rendre leur exécution plus rapide, à l'époque où celles-ci servaient encore (MS-DOS). La mémoire RAM est en effet bien plus rapide que la mémoire (EEP)ROM dans laquelle le BIOS est stocké. Certaines options du BIOS, souvent nommées BIOS memory shadowing (ou autres noms ressemblants), permettent justement de copier le BIOS dans une partie de la mémoire RAM afin d'accélérer l'exécution de ses routines. Sachez que ce genre de choses est inutile depuis que le BIOS ne sert plus qu'à l'allumage de l'ordinateur.
On va lister quelques routines importantes. Dans ce qui suit, l'adresse mentionnée sera l'adresse stockée dans le vecteur d'interruption de ladite routine (relisez plusieurs fois si besoin). Il faudra cependant être vigilant : la grande majorité de ces interruptions est détournée par notre système d'exploitation, et n'est utilisée que lors de l'allumage de l'ordinateur. En fait, dès qu'il existe un driver pour un périphérique quelconque, sachez que les routines du BIOS sont complètement détournées par votre OS.
Une grande partie de ces routines ont besoin qu'on leur fournisse des paramètres, des informations pour qu'elles fassent ce qu'elles doivent faire. Par exemple, une routine devant afficher une lettre à l'écran aura besoin qu'on lui donne en entrée la lettre à afficher. Pour chaque routine, il suffira de copier ces paramètres de la mémoire vers de petites mémoires ultra-rapides intégrées dans le processeur qu'on appelle les registres. Chacun de ces registres possède un nom qui permet de l'identifier : AX, AH, AL… Pour chaque routine, chaque registre contiendra une donnée ayant une signification bien précise pour notre routine. Ce seront souvent les registres AH ou AL du processeur.
Dans ce qui suit, nous listerons quelques routines pour montrer un peu leur utilité. On nommera ces routines.
Chaque routine est identifiée par :
l'identifiant INT, qui sert à préciser que l'on souhaite exécuter une interruption ;
les registres, utilisés pour configurer notre routine ;
un numéro, qui permet d'identifier la routine à exécuter.
En effet, nos routines sont numérotées. Mais attention : ce numéro est un nombre hexadécimal ! En clair, INT 10 n'est pas la dixième interruption, mais la seizième. Pour ceux qui ne connaissent pas l'hexadécimal, rassurez-vous, vous pourrez comprendre la suite sans problème.
INT 10h : affichage
Nos cartes graphiques actuelles contiennent toutes une mémoire ROM (voire EEPROM) contenant elle-même des tas de routines capables de faire afficher du texte et des graphismes monochromes ou 256 couleurs à l'écran. Dans les cartes graphiques compatibles avec les formats VGA/ESA, ce firmware (programme intégré au composant — le BIOS est un firmware), appelé improprement BIOS vidéo, est localisé dans l'espace d'adressage aux adresses 000C 0000 ou 000E 0000. Lors du démarrage de l'ordinateur, ce sont ces routines qui sont utilisées pour gérer l'affichage avant que le système d'exploitation ne lance les drivers graphiques. Ces fonctions sont extrêmement basiques, ce qui fait que les graphismes affichés à l'écran ont cet aspect si caractéristique, moche, faisant penser à l'informatique des années 1950…
Mais faites attention : une fois le pilote de la carte graphique démarré, ces fonctions de base ne servent plus ; le système d'exploitation, le driver vidéo et les programmes lancés gèrent eux-mêmes ce genre de choses et détournent sans vergogne les interruptions qu'ils souhaitent.
Si aucune ROM vidéo n'est détectée, le BIOS peut quand même communiquer directement avec la carte graphique grâce à une routine qu'il possède. Cette routine a plusieurs fonctions différentes, et peut tout aussi bien envoyer un caractère à l'écran que renvoyer la position du curseur. Pour spécifier le traitement à effectuer, on doit placer une certaine valeur dans le registre AH du processeur : la routine est programmée pour déduire le traitement à effectuer uniquement à partir de la valeur du registre AH.
Exemple :
INT 0x10 | Valeur à placer dans le registre AH | Description |
---|---|---|
INT 0x10 | 0x00 | Permet de régler la résolution et le nombre de couleurs de l'écran. |
INT 0x10 | 0x01 | Place le curseur de la souris à la position voulue sur l'écran. |
INT 0x10 | 0x03 | Renvoie la position qu'occupe le curseur de la souris à l'écran. |
INT 0x10 | 0x0E | Affiche un caractère ASCII. |
INT 11h
Cette routine renvoie un nombre codé sur 16 bits contenant des informations sur certains périphériques présents sur l'ordinateur.
INT 12h
Cette routine ne prend aucun paramètre et renvoie la taille de la mémoire en kilo-octets dans le registre AX.
INT 13h : mémoires de masse
Une routine du BIOS permet de lire ou d'écrire sur le disque dur ou sur une disquette. Cette routine lui sert à lire les premiers octets d'un disque dur afin de pouvoir charger le système d'exploitation, mais on verra cela au chapitre suivant.
Comme la routine INT 10, elle est polyvalente et on doit spécifier le traitement à effectuer en mettant le registre AH à une valeur unique pour chaque traitement.
Exemple :
INT 0x13 | Valeur à placer dans le registre AH | Description |
---|---|---|
INT 0x13 | 0x00 | Réinitialise le disque sélectionné. |
INT 0x13 | 0x01 | Vérifie l'état du disque. |
INT 0x13 | 0x02 | Lit une donnée à partir du disque sélectionné. |
INT 0x13 | 0x03 | Écrit une donnée sur le disque sélectionné. |
… | … | … |
Cette routine était utilisée par les systèmes d'exploitation du style MS-DOS pour lire ou écrire sur le disque dur. Mais cela appartient à l'histoire, et les OS actuels n'hésitent plus à détourner ces interruptions pour fournir des méthodes de gestion du disque bien plus efficaces.
INT 14h : port série
Cette interruption était utilisée pour communiquer avec le port série RS232 de notre ordinateur. Comme les autres, elle est configurée par une valeur dans le registre AH.
INT 0x14 | Valeur à placer dans le registre AH | Description |
---|---|---|
INT 0x14 | 0x00 | Initialise le port série. |
INT 0x14 | 0x01 | Émet un caractère sur le port série. |
INT 0x14 | 0x02 | Réceptionne un caractère. |
INT 0x14 | 0x03 | Renvoie l'état du port série (occupé, libre…). |
INT 15h : fonctions système avancées
Cette interruption a des fonctions diverses et variées, toutes plus ou moins rattachées à la gestion du matériel. Le BIOS était autrefois en charge de la gestion de l'alimentation de notre ordinateur : il se chargeait de la mise en veille, de réduire la fréquence du processeur, d'éteindre les périphériques inutilisés. Pour cela, la routine INT 15 était utilisée. Ses fonctions de gestion de l'énergie étaient encore utilisées jusqu'à la création de Windows 95. De nos jours, avec l'arrivée de la norme ACPI, le système d'exploitation gère tout seul la gestion de l'énergie de notre ordinateur et cette routine est donc obsolète.
À toute règle, il faut une exception : cette routine est utilisée par les systèmes d'exploitation modernes à leur démarrage afin d'obtenir une description correcte et précise de l'organisation de la mémoire de l'ordinateur. Pour cela, nos OS configurent cette routine en plaçant la valeur 0x0000e820 dans le registre EAX. C'est une des très rares exceptions à la règle : les routines du BIOS sont obsolètes.
INT 16h : clavier
Notre clavier communique avec notre ordinateur en envoyant trois sortes de signaux, qui permettent de déterminer ce que l'utilisateur fait avec son clavier. Chacun de ces signaux génère une interruption, qui sera interprétée par la routine adéquate.
Trois messages principaux sont utilisés :
touche appuyée : signal spécifiant qu'on a appuyé sur une touche ;
touche relâchée : signal spécifiant qu'on a relâché une touche ;
et touche répétée, qui spécifie que la touche du clavier est restée enfoncée depuis le dernier envoi de message. Ce message sert quand on appuie continument sur une touche sans la relâcher.
Chacun de ces signaux spécifie la touche en question, évidemment.
Cette routine permet de gérer une partie du clavier, et souvent de le configurer. Comme la routine INT 10, elle est très polyvalente et peut effectuer de nombreux traitements : on précise le traitement voulu en mettant le registre AH à une certaine valeur.
INT 0x16 | Valeur à placer dans le registre AH | Description |
---|---|---|
INT 0x16 | 0x00 | Identifie la touche tapée au clavier. Cette fonction est bloquante : on doit attendre la fin de l'exécution de cette routine avant de pouvoir faire exécuter quoi que ce soit d'autre par le processeur. |
INT 0x16 | 0x01 | Identifie la touche tapée au clavier. Cette routine est non bloquante. |
INT 0x16 | 0x02 | Spécifie le nombre d'envois maximal de messages (comme « touche répétée ») par seconde. |
Cette routine est utilisée tant que le système d'exploitation n'a pas démarré, c'est pour cela que vous pouvez utiliser le clavier pour naviguer dans l'écran de configuration de votre BIOS.
En revanche, aucune routine standard ne permet la communication avec la souris : il est impossible d'utiliser la souris dans la plupart des BIOS. Certains BIOS possèdent malgré tout des routines capables de gérer la souris, mais ils sont très rares. Encore une fois, ces routines sont détournées par le système d'exploitation à son lancement.
INT 17h : port parallèle
Cette routine permet de communiquer avec une imprimante sur le port parallèle de l'ordinateur. Comme les autres, on la configure avec le registre AH.
INT 0x17 | Valeur à placer dans le registre AH | Description |
---|---|---|
INT 0x17 | 0x00 | Imprime un caractère. |
INT 0x17 | 0x01 | Initialise l'imprimante. |
INT 0x17 | 0x02 | Vérifie l'état de l'imprimante (en cours d'impression, libre…). |
INT 19h
C'est une surprise !
Vous verrez dans le chapitre sur le démarrage d'un ordinateur.
Conclusion
Comme vous le voyez, le BIOS fournit beaucoup de routines qui étaient au départ censées faciliter la gestion du matériel. Il y a longtemps, cela simplifiait la conception des systèmes d'exploitation, et de nombreux OS, tel MS-DOS, utilisaient ces fonctions. Mais de nos jours, connaître ce genre de choses est plus synonyme d'archéologie expérimentale qu'autre chose.