27
LYCEE Catherine et Raymond JANOT de SENS COURS de TURBO – C CHAPITRE 15 Les Interruptions : Structure du 8088 – Appels du BIOS Jean - Luc PELLARD

COURS de TURBO – C CHAPITRE 15 - tido94.free.frtido94.free.fr/Lyc%E9e/Cours/Cours%20C/Chapitre... · Les microprocesseurs 8086, 8088 et 80286 font partie d'une même famille de

Embed Size (px)

Citation preview

LYCEE Catherine et Raymond JANOT de SENS

COURS de TURBO – C

CHAPITRE 15

Les Interruptions : Structure du 8088 – Appels du B IOS

Jean - Luc PELLARD

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 2

Les Interruptions : Structure du 8088 - Appels du BIOS.

extrait du livre Turbo-C de Leblanc

Les microprocesseurs 8086, 8088 et 80286 font partie d'une même famille de microprocesseurs à registres de 16 bits. Le 8088, que l'on retrouve sur l'IBM-PC, se distingue du 8086 par la taille de son bus de données (8 bits pour le 8088), ce qui signifie que le 8088 transfère les données entre microprocesseur et mémoire sur 8 fils. Le 8088 amène un entier (16 bits) dans un registre en deux opérations alors que les 8086 et 80286 le font en une seule. Cette opération, dite multiplexage, est entièrement prise en charge par l'électronique et reste transparente pour le programmeur. Le 80286, qui équipe l'IBM-AT, intègre, outre le 8086, plusieurs autres composants. Du point de vue de la programmation ils peuvent être considérés comme équivalents.

15.1) Les registres du 8086. La taille des registres du 8086 est de 16 bits. Certains d'entre eux (AX, BX, CX et DX peuvent être considérés comme formés de 2 registres de 8 bits. Par exemple, AX peut être décomposé en AH et AL , AH étant formé de la partie haute (high) de AX et AL de la partie basse (low).

AX AH AL accumulateur BX BH BL base registres CX CH CL compte généraux DX DH DL donnée

SP pointeur de pile BP base pointeurs SI source et index DI destination IP compteur ordinal STATUS registre d'état CS segment de code DS segment de données registres de ES segment "extra" segmentation SS segment de pile

Les registres AX, BX, CX et DX sont du registres généraux même si certaines instructions du microprocesseur leur donnent une signification particulière. SI et DI sont associés à des opérations de traitement de chaînes de caractères (Source Index ou index d'origine et Destination Index ou index de destination) mais peuvent également être

15.

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 3

considérés comme registres à usage général. Les variables registres sont gardées par TURBO-C dans les registres SI et DI, du moins si cela est possible. SP (Stack Pointer ou indicateur de sommet de pile) ne peut être utilisé comme registre à usage général. Mais BP (Base Pointer ou registre de base) peut l'être. Le registre IP (Instruction Pointer « ou compteur ordinal) contient l'adresse de la prochaine instruction à effectuer. Le registre STATUS contient les indicateurs d'état du microprocesseur. Il a le format suivant :

b15 b8 b7 b0 O D I T S Z A P C

avec :

O : débordement (overflow) D : direction I : interruption T : fonctionnement pas à pas S : signe Z : zéro A : auxiliary carry P : parité C : retenue (carry)

Quatre registres jouent un rôle fondamental lors des calculs d'adresse :

CS : Code Segment DS : Data Segment (ou segment de données) SS : Stack Segrnent (ou segment de pile) ES : Extra Segment

Ces registres ont été introduits pour permettre d'adresser plus de 64 KB (de code ou de données), étant donné la limite à 64 K d'un registre de 16 bits. Le 8086 dispose de 20 lignes d'adresse, ce qui lui permet d'adresser jusqu'à 1 MB (en mode protégé, le 80286 pourrait adresser, mais PC-DOS ne le fait pas, par tâche, jusqu'à 1 gigabyte de mémoire virtuelle, la mémoire physique accessible étant de 16 MB). Un programme peut être décomposé en 3 parties : le code, les données et la pile qui peuvent être physiquement séparés, contigus ou occuper la même zone de la mémoire. En outre, un programme peut accéder à une quatrième partie, dite "extra", qui peut être utilisée comme une zone secondaire de données à usage général. Ces différentes parties sont appelées segments et l'adresse d'un segment chargé en mémoire se trouve dans un registre spécial, le registre segment (CS pour le segment de code, DS pour le segment de données). L'adresse de début d'un segment doit être un multiple de 16 (il doit coïncider avec un début de paragraphe, la mémoire étant logiquement considérée comme formée de blocs de 16 octets appelés paragraphes).

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 4

La taille d'un segment peut varier de 1 octet à 64 KB. Deux valeurs sont utilisées pour faire référence à une adresse dans un segment :

• l'adresse de début du segment (divisée par 16) • un déplacement à l'intérieur du segment

Ces deux valeurs sont codées sur 16 bits. Elles constituent l'adresse segmentée d'une adresse réelle. Une adresse segmentée est dite normalisée si le déplacement est inférieur à 16. Les registres segments définissent la portion de la mémoire accessible à un instant donné. Chaque registre contient l'adresse de base d'un segment. Le CPU construit l'adresse réelle de la façon suivante :

adresse réelle = ( segment * 16 ) + déplacement

15.2) Les interruptions du 8086. Les interruptions logicielles jouent un rôle considérable dans l'architecture du 8086. Le principe des interruptions est antérieur au 8086 mais était auparavant limité aux interruptions matérielles. Une interruption est un signal généré par un périphérique qui provoqué un déroutement automatique des instructions en cours d'exécution. Les principales interruptions sont :

interruption périphérique 10 vidéo 13 disque 14 ligne série 16 clavier 17 imprimante 8 horloge

Le principe a été étendu aux interruptions logicielles qui sont provoquées par programme. A chaque interruption (logicielle ou matérielle) est associé un numéro de 0 à 255 qui sera spécifié comme argument dans les fonctions C qui réalisent les interruptions logicielles. Les 1024 premiers octets de la mémoire (de 0x0000 à 0x3FF) sont constitués de 256 zones de 4 octets, chacune de ces zones étant consacrée à une interruption (la première zone à l'interruption 0, la deuxième à l'interruption 1, ...).

$ 0000 IP : CS interruption 0 $ 0004 IP : CS interruption 1 $ 0008 IP : CS interruption 2 $ 000C IP : CS interruption 3 $ 00FF IP : CS interruption 255 ou 0xFF

Ces 256 zones de 4 octets forment la table des interruptions .

Chaque zone est à son tour divisée en deux zones de 2 octets :

IP : CS IP est un déplacement et CS est une valeur de code segment ou, pour certaines interruptions, de data segment.

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 5

Une interruption est générée par un périphérique (câblé pour générer un numéro déterminé d'interruption) ou est générée par programme. Ainsi, environ 18 fois par seconde, le circuit d'horloge (timer, mais ne "pas confondre avec celui qui cadence le microprocesseur) génère un signal qui provoque une interruption (interruption 8). Le programme en cours d'exécution est interrompu (l'instruction machine en cours d'exécution s'achève normalement mais la suivante n'est pas entreprise). L'adresse de la prochaine instruction (du programme) à exécuter ainsi que les indicateurs d'état sont automatiquement sauvés sur la pile. L'électronique du microprocesseur provoque alors (tout aussi automatiquement, c’est-à-dire sans qu'il y corresponde la moindre instruction) un branchement à l'adresse déterminée par CS:IP dont les valeurs se trouvent dans l'entrée 8 du vecteur d'interruption. A cet emplacement de la mémoire (et dans les octets qui suivent) doit se trouver la procédure (la "routine" comme dirent les programmeurs en assembleur) de traitement de l'interruption. Lorsque cette procédure exécute l'instruction IRET (qui est une instruction de la machine qui signifie RETour d'interruption ), le système restaure (toujours aussi automatiquement) les indicateurs d'état qui avaient été sauvegardés sur la pile et poursuit l'exécution du programme là où il avait été interrompu. Une interruption logicielle (soft interrupt) est une interruption générée non par un dispositif câblé mais par le programme lui-même. Une interruption logicielle n'est pas très différente d'un appel (très souple) de procédure. Elle sert à exécuter une fonction du système (obtenir la date du système, cacher un fichier, lire directement un secteur de disque, ...) que pour ses besoins propres, MICROSOFT a déjà écrit et mis à la disposition dos utilisateurs. Pour les utiliser, le programmeur n'a pas à se préoccuper de leur localisation en mémoire. Il doit uniquement connaître le numéro d'interruption logicielle (lors de la phase d'initialisation du système après mise sous tension, le système initialise les vecteurs d'interruption pour faire correspondre à chaque interruption l'adresse de la mémoire où se trouve le code d'exécution des interruptions).

15.3) Génération d'interruptions logicielles. La plupart des interruptions logicielles s'attendent à trouver des valeurs particulières (fonctions de chaque interruption) dans certains registres. A cet effet, des structures doivent être déclarées. C'est à partir de celles-ci que les registres de la machine sont chargés lors de l'appel de l'interruption logicielle et c'est dans celles-ci que les registres sont placés à la sortie de l'interruption. On n'accède donc pas aux registres réels de la machine mais à des images de ceux-ci. Une possibilité existe cependant en TURBO-C d'accéder à tout moment aux registres de la machine : ce sont les variables prédéfinies _AX, _BX, ...

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 6

a) structures définies dans la bibliothèque dos.h :

struct WORDREGS { /* registres généraux */ unsigned int ax , bx , cx , dx , si , di , cflag , flags ; } ; struct BYTEREGS { /* registres de 8 bits */ unsigned char al , ah , bl , bh , cl , ch , dl , dh ; } ; struct SREGS { /* registres segments */ unsigned int es , cs , ss , ds ; } ; union REGS { struct WORDREGS x ; struct BYTEREGS h ; } ; union REGPACK { unsigned r_ax , r_bx , r_cx , r_dx ; unsigned r_bp , r_si , r_di , r_ds , r_es , r_rings ; } ;

Pour chaque interruption, les registres BP, DS et ES sont sauvegardés. b) fonctions d'interruptions logicielles :

# include <dos.h> int int86 ( numint , rin , rout ) int numint ; /* numéro d'interruption logiciell e */ union REGS * rin ; /* pointeur vers structure con tenant les registres généraux de 16 bits en entrée */ union REGS * rout ; /* idem pour sortie */

génère l'interruption logicielle de numéro numint et retourne le contenu du registre AX au retour de 1'interruption. Les registres sont chargés par la fonction int86 à partir la structure rin et recopiés dans la structure rout à la sortie de la procédure de traitement de l'interruption. L'indicateur d'état (carry) est copié dans rout.x.cflag (comme le fait le compilateur C de MICROSOFT alors que pour LATTICE, l'indicateur d'état est la valeur retournée par fonction).

int int86x ( numint , rin , rout , segs ) int numint ; /* numéro d'interruption */ union REGS * rin ; /* pointeur vers structure con tenant les registres généraux utilisés en entrée */ union REGS * rout ; /* idem pour sortie */ struct SREGS * segs ; /* pointeur vers structure co ntenant les registres segments utilisés en entrée */

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 7

Cette fonction charge également les images des registres DS et ES. Au retour, DS est sauvegardé. Elle doit être utilisée lorsque l'interruption logicielle se sert d'autres segments de données ou "extra" que ceux utilisés par le programme.

void intr ( numint , pack ) int numint ; /* numéro d'interruption */ struct REGPACK * pack ;

génère l'interruption logicielle. Les valeurs de la structure sont copiées dans les registres avant l'interruption. Les registres sont copiés dans la structure à la sortie.

void geninterrupt ( numint ) void disable ( ) ; void enable ( ) ;

disable ( ) masque toutes les interruptions, sauf NMI tandis que enable ( ) les autorise. geninterrupt ( ) génère une interruption sans copie de registres. Toutes ces "fonctions" sont en fait des macros, c’est-à-dire que le compilateur ne fait que générer le code machine correspondant.

struct SREGS * segs ; segread ( segs )

place les valeurs des registres de segment dans la structure sregs. S'utilise en préparation de int86x.

15.4) Accès aux ports d'entrée / sortie. Les boîtiers d'entrée / sortie sont reliés au microprocesseur par l'intermédiaire de "portes" (ou port ). Chaque port est désigné par une adresse. Les ports sont programmables à l'aide d'instructions (langage machine IN et OUT). Les fonctions inport et inportb ainsi que outport et outportb du langage C permettent l'accès direct aux ports d'entrée / sortie.

# include <dos.h> int inport ( port ) /* lit un mot du port */ int inportb ( port ) /* lit un octet du port */ void outport ( port , mot ) /* écrit un mot sur le port */ void outportb ( port , octet ) /* écrit un octet * / int port ; /* adresse du port */ int mot ; /* mot à écrire ou à lire sur port */ char octet ; /* octet à écrire ou à lire s ur port */

Pour assurer une compatibilité avec MICROSOFT et LATTICE, le TURBO-C a néanmoins prévu les fonctions inp et outp dans dos.h

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 8

15.5) Accès direct à la mémoire. Les fonctions peek et poke permettent de lire ou d'écrire à n'importe quelle adresse de la mémoire (en particulier dans la mémoire vidéo ou les tables du DOS), cette adresse étant spécifiée par le couple ( segment : déplacement ).

# include <dos.h> int peek ( segment , depl ) /* lit 16 bits */ char peekb ( segment , depl ) /* lit un caractère */ int segment ; /* adresse du segment (en par agraphes) */ unsigned depl ; /* déplacement à l'intérieur du segment */

transfère un mot ou un octet de données depuis l'adresse ( segment : depl ). Ce mot ou cet octet est retourné par la fonction.

void poke ( segment , depl , mot ) /* écrit un mo t */ void pokeb ( segment , depl , octet ) /* écrit un caractère */

Ces fonctions sont des macros quand dos.h a été inclus. peek et poke se comportent différemment des fonctions de même nom de la bibliothèque de LATTICE.

# include <string.h> movedata ( srcseg , srcoff , destseg , destoff , no ctets ) int srcseg ; /* segment origine */ int srcoff ; /* déplacement dans segment orig ine */ int destseg ; /* segment destination */ int destoff ; /* déplacement dans segment des tination */ unsigned int octets ; /* nombre d'octets à transf érer */

copie noctets d'origine à destination. Si l'origine et/ou la destination se trouvent dans une variable du programme, utiliser préalablement segread pour obtenir la valeur du registre DS (ou accès par _DS, mais cette possibilité est propre à TURBO-C). Utiliser FP_SEC et FP_OFF pour obtenir les valeurs de segment et de déplacement d'un pointeur codé sur 32 bits.

15.6) Les fonctions DOS. Les interruptions 0x20 à 0x3F sont réservées à l'usage de DOS. Parmi celles-ci, l'interruption 0x21 offre une grande variété de fonctions. Une fonction particulière est sélectionnée en plaçant une valeur déterminée dans le registre AH. Ces différentes fonctions sont partiellement expliquées dans ce qui suit. La valeur à placer dans les registres (en particulier AH) est indiquée dans la notation hexadécimale. En cas d'erreur, l'indicateur de report (carry) est positionné et le registre AX contient le code d'erreur (en décimal dans le tableau suivant).

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 9

code signification code signification

1 numéro de fonction non valide 20 unité de disque non valide 2 fichier introuvable 21 l'unité n'est pas prête 3 chemin introuvable 22 commande de disque non valide 4 trop de fichiers ouverts 23 erreur de CRC 5 accès refusé 24 longueur non valide (disque) 6 numéro de fichier non valide 25 erreur de recherche (seek sur disque) 7 blocs de contrôle de mémoire

détruits 26 disque non MS-DOS

8 mémoire insuffisante 27 secteur introuvable 9 adresse de bloc mémoire non valide 28 plus de papier 10 environnement non valide 29 erreur d'écriture 11 format non valide 30 erreur de lecture 12 mode d'accès non valide 31 défaillance générale 13 données non valides 32 erreur de partage de fichier 15 unité non valide 33 problème de verrouillage 16 tentative de suppression du

répertoire en cours 34 mauvais disque

17 pas la même unité 35 pas de FCB 18 plus de fichier 19 disquette protégée en écriture

Les erreurs 50 à 88 sont relatives au travail en réseau de plusieurs ordinateurs.

15.7) Présentation des interruptions les plus courantes . L 'annexe en fin de ce chapitre présente un certain nombre d'interruptions. La liste n'est pas exhaustive. Pour plus de complément, consultez la bible du PC par exemple. Le livre "Turbo-C" de Leblanc donne beaucoup d'informations sur le traitement des interruptions.

15.8) Exemples de programmes mettant en application différentes interruptions et les problèmes d'accès directs dans la mémoire vidéo.

a) Copie sur imprimante après vérification de sa disponibilité :

�� /* programme d'interruption n°1 : copie écran sur i mprimante après vérification de la disponibilité de celle-ci */ # include <conio.h> # include <dos.h> # include <stdio.h> void ImprimerEcran ( void ) { /* impression sur imprimante (LPT1 par défaut) par l'interruption BIOS n° 5 */ geninterrupt ( 0x05 ) ;

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 10

} unsigned DisposerImprimante ( void ) { /* structures contenant l’image des registres du m icroprocesseur */ union REGS regin , regout ; /* Lecture de la disponibilité de l'imprimante */ regin.h.ah = 0x02 ; /* numéro de la fonct ion */ regin.x.dx = 0x00 ; /* sélection du port LPT1 */ int86 ( 0x17 , & regin , & regout ) ; /* interrup tion n° 17 */ /* renvoie 1 si l’imprimante est prête à imprimer, 0 sinon */ return ( ( regout.h.ah ) == 0x90 ) ? 1 : 0 ; } void main ( void ) { /* prototypes des fonctions utilisées */ void ImprimerEcran ( void ) ; unsigned DisposerImprimante ( void ) ; clrscr ( ) ; textcolor ( LIGHTGREEN ) ; gotoxy ( 15 , 2 ) ; cprintf ( "******************* *******************" ) ; gotoxy ( 15 , 3 ) ; cprintf ( "* Copie de cet écran sur l'imprimante LPT1 *" ) ; gotoxy ( 15 , 4 ) ; cprintf ( "******************* *******************" ) ; gotoxy ( 15 , 10 ) ; textcolor ( WHITE ) ; cprintf ( "Appuyer sur une touche pour imprimer ce tte page écran..." ) ; fflush ( stdin ) ; getch ( ) ; if ( DisposerImprimante ( ) ) { gotoxy ( 20 , 18 ) ; textcolor ( YELLOW ) ; cprintf ( "L'imprimante est disponible : impressi on en cours.." ) ; ImprimerEcran ( ) ; gotoxy ( 20 , 20 ) ; cprintf ( "Impression termin ée !" ) ; } else { gotoxy ( 20 , 18 ) ; textcolor ( LIGHTRED ) ; cprintf ( "L'imprimante n'est pas disponible actu ellement... " ) ; } gotoxy ( 20 , 22 ) ; textcolor ( LIGHTCYAN ) ; cprintf ( "Appuyer sur sur touche pour quitter ce programme" ) ; fflush ( stdin ) ; getch ( ) ; normvideo ( ) ; }

b) Détermination du lecteur actif : �� /* programme n°2 sur les interruptions, il affiche le n° du lecteur actif */ # include <dos.h>

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 11

# include <stdio.h> # include <conio.h> void main ( void ) { /* structures contenant l’image des registres du m icroprocesseur avant et après l'appel de l'interruption */ union REGS inregs , outregs ; clrscr ( ) ; inregs.h.ah = 0x19 ; /* N° fonction */ int86 ( 0x21 , & inregs , & outregs ) ; printf ( "\nLe lecteur actif est %c" , ( 65 + outr egs.h.al ) ) ; fflush ( stdin ) ; getch ( ) ; }

c) Affichage de la date et de l'heure :

��� /* programme interruption n°3 : il lit la date et l 'heure du système */ # include <stdio.h> # include <conio.h> # include <dos.h> void main ( void ) { /* structures contenant l’image des registres du m icroprocesseur avant et après l'appel de l'interruption */ union REGS inregs , outregs ; clrscr ( ) ; /* lecture de la date du système */ inregs.h.ah = 0x2A ; /* N° de la fonction */ int86 ( 0x2l , & inregs , & outregs ) ; /* générat ion de l'interrup. */ switch ( outregs.h.al ) { case 0 : printf ( "\nDimanche" ) ; break ; case 1 : printf ( " \nLundi" ) ; break ; case 2 : printf ( "\nMardi" ) ; break ; case 3 : printf ( "\nMercredi" ) ; break ; case 4 : printf ( "\nJeudi" ) ; break ; case 5 : printf ( "\nVendredi" ) ; break ; case 6 : printf ( "\nSamedi" ) ; } printf ( "\nLa date est %d / %d / %d" , outregs.h. dl , outregs.h.dh , outregs.x.cx ) ; /* lecture de l'heure système */ inregs.h.ah = 0x2C ; int86 ( 0x2l , & inregs , & outregs ) ; /* générat ion de l'interrup. */ printf ( "\nIl est %d h %d min %d s %d cent" , out regs.h.ch , outregs.h.cl , outregs.h.dh , outrgs.h.dl ) ;

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 12

fflush ( stdin ) ; getch ( ) ; }

d) Gestion de l'écran : �� /* programme interruption n°4 : écran couleur */ # include <dos.h> void main ( void ) { union REGS regsIn , regsOut ; int couleur ; /* écran en mode couleurs 80 x 25 */ regsIn.h.ah = 0 ; /* n° fonction de l'interru ption. */ regsIn.h.al = 3 ; /* mode couleurs 80*25 */ int86 ( 0x10 , & regsIn , & regsOut ) ; /* bordure en rouge (en mode EGA)*/ couleur = 4 ; /* dans premier groupe de 8 cou leurs */ /* la couleur de bordure peut être sélectionnée da ns le port 0x3D9 */ outport ( 0x3D9 , couleur ) ; /* couleur de fond */ regsIn.h.ah = 6 ; /* fonction de défilement * / regsIn.h.al = 0 ; /* toute la page */ regsIn.h.ch = regsIn.h.cl = 0 ; /* coordonnées du coin supérieur gauche */ regsIn.h.dh = 24 ; regsIn.h.dl = 79 ; /* coordonnées du coin inf érieur droit */ regsIn.h.bh = 0x22 ; /* attribut pour la ligne effacée : vert sur fond vert */ int86 ( 0x10 , & regsIn , &regsOut ) ; /* curseur à la dixième ligne, première colonne */ regsIn.h.ah = 2 ; /* n° de la fonction */ regsIn.h.dh = 9 ; /* n° de la ligne */ regsIn.h.dl = 0 ; /* n° de la colonne */ regsIn.h.bh = 0 ; /* n° de la page */ int86 ( 0x10 , & regsIn , & regsOut ) ; /* écriture d'une ligne de Z en rouge sur fond bru n */ regsIn.h.ah = 9 ; /* n° de la fonction */ regsIn.h.al = 0x5A ; /* caractère écrit */ regsIn.h.bh = 0 ; /* n° de page */ regsIn.h.bl = 0x6C ; /* lettre rouge clair sur f ond brun : 01101100 */ regsIn.x.cx = 80 ; /* nbre de caractères à éc rire */ int86 ( 0x10 , & regsIn , & regsOut ) ; fflush ( stdin ) ; getch ( ) ; /* remettre l'écran en N&B */ regsIn.h.ah = 0 ; /* N° de fonction */ regsIn.h.al = 2 ; /* mode 80*5 N&B */ int86 ( 0x10 , & regsIn , & regsOut ) ;

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 13

}

e) Accès direct dans la mémoire vidéo : Ce programme permet d'écrire à l'écran en accédant directement la mémoire vidéo (utilisation de pook, move etc... ) ; ce programme :

• écrit une chaîne "BONJOUR" en haut à gauche de l'écran "en mode normal brillant". • écrit un "G" en haut à droite de l'écran "en vidéo inverse clignotante". • écrit un "A" en bas à droite de l'écran."en mode normal". • copie 2 caractères de la première ligne. • recopie plusieurs fois la chaîne "BONJOUR".

/* programme interruption n°5 : accès direct à la m émoire vidéo */ # include <conio.h> # include <dos.h> # include <stdio.h> # include <string.h> void main ( void ) { /****************** Variables de la fonction Main( ) ******************/ char bonjour [ ] = "BONJOUR" ; /* bonjour [ ] idem que * bonjour */ char * ptr1 ; char far * ptr2 ; /* far pour aller aux adresse s mémoire vidéo */ /********************* Programme principal ******* *********************/ /* une info mémoire = 2 octets ( caractère + attri but affichage ) */ clrscr ( ) ; /* Ecriture de la chaîne "BONJOUR" caractère par c aractère en accédant directement à la mémoire vidéo */ for ( ptr1 = bonjour , ptr2 = ( char far * ) 0xb80 00000L ; * ptr1 ; ) /* casting sinon warning nonportable pointer conve rsion */ { * ptr2 ++ = * ptr1 ++ ; * ptr2 ++ = 0x0A ; /* attribut couleur vert br illant */ } /* Ecriture du caractère "A" en bas à droite de l' écran */ ptr2 = ( char far * ) 0xB8000F9EL ; * ptr2 = 'A' ; * ( ptr2 + 1 ) = 0x07 ; /* gris */ /* Ecriture du caractère "G" en haut à droite de l 'écran */ pokeb ( 0xB800 , 0x009C , 'G' ) ; pokeb ( 0xB800 , 0x009D , 0xF0 ) ; /* gris inverse vidéo clignotant */ /* Saisie de 2 caractères dans la mémoire vidéo et affichage */ printf ( "\n\nLes 2 caractères extrêmes de la 1ere ligne sont : %c, %c", peekb ( 0xB800 , 0x0000 ) , peekb ( 0xB800 , 0x00 9C ) ) ; /* saisie de la chaîne "BONJOUR" et recopie 15 foi s à l'écran en accédant directement à la mémoire vidéo */

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 14

for ( ptr2 = (char far *) 0xB80000280L ; ptr2 < ( char far * ) 0xb8000370L ; ptr2 += 16 ) { movedata ( 0xB800 , 0x0000 , 0xB800 , FP_OFF ( pt r2 ) , 14 ) ; delay ( 400 ) ; } fflush ( stdin ) ; getch ( ) ; }

15.9) Gestion de l'écran en mode graphique. Ce mode (encore appelé APA All Point Adressable) n'est possible qu'avec la carte adaptateur couleur / graphique (et bien sûr la carte EGA). Deux résolutions sont possibles :

♦ la haute résolution (en noir et blanc uniquement) où une image peut être composée de 640

points horizontaux et de 200 points verticaux. Comme 640 * 200 = 128.000 bits, soit environ 16 Koctets, ce qui est la taille de la mémoire vidéo, un point doit être codé sur 1 bit (ce qui correspond à point visible / point non visible).

♦ la résolution moyenne (320 points horizontaux et 200 points verticaux), soit en noir et blanc soit en couleurs.

Quelle que soit la résolution le point (0, 0) désigne le point supérieur gauche de l'écran. Dans le mode moyenne résolution, la "valeur" d'un point est codée sur 2 bits, ce qui permet de l'afficher en couleurs de la manière suivante :

♦ 0 indique que le point est de la couleur du fond (back ground color), ce qui signifie qu'il n'est pas visible.

♦ 1, 2 ou 3 : le point est affiché dans une des 3 couleurs, différentes de celle du fond. Ces 3 couleurs dépendent du choix d'un ensemble de couleurs (palette). La fonction B permet de choisir la couleur de fond (16 couleurs possibles : noir, bleu, ..., jaune, blanc) ainsi que la palette. Les différentes couleurs d'avant-plan (foreground) pour chacune des 2 palettes sont :

couleurs 1 2 3

palette n° 0 vert rouge jaune palette n° 1 cyan magenta blanc

15.10) Affichages en couleurs dans le mode alphanumérique.

♦ Un moniteur couleur peut être connecté à la carte adaptateur couleur / graphique. ♦ Une couleur est obtenue par combinaison de 3 couleurs de base (rouge, vert et bleu) et

d'une intensité d'affichage. ♦ Un caractère peut dès lors être affiché dans une parmi 16 couleurs possibles, le fond

pouvant prendre 8 couleurs.

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 15

a) Attribut couleur en mode alphanumérique :

bit 7 6 5 4 3 2 1 bit 0

R V B I R V B clignote-

ment couleur du fond couleur du caractère

b) La couleur du caractère est l'une des 16 couleurs suivantes : Si le bit I de la couleur du caractère vaut 0, la couleur du fond est l'une des huit premières. Sinon, elle est l'une des huit dernières.

I R V B couleur I R V B couleur 0 0 0 0 noir 1 0 0 0 gris foncé 0 0 0 1 bleu 1 0 0 1 bleu clair 0 0 1 0 vert 1 0 1 0 vert clair 0 0 1 1 cyan (bleu vert) 1 0 1 1 cyan clair 0 1 0 0 rouge 1 1 0 0 rouge clair 0 1 0 1 magenta (rouge violacé) 1 1 0 1 magenta clair 0 1 1 0 brun 1 1 1 0 jaune 0 1 1 1 gris clair 1 1 1 1 blanc

c) La couleur de bordure : La couleur de bordure peut être sélectionnée par programmation du port d'adresse 0x3D9.

bit 7 6 5 4 3 2 1 bit 0 0x3D9 R V B

bits 0, 1, 2 : ensemble R, V et B des couleurs de fond. bit 3 : utilisé en mode graphique moyenne résolution (320 x 200) pour sélectionner le

premier ou le second ensemble de 8 couleurs. bit 4 : idem, mais en mode alphanumérique. bit 5 : sélection du mode moyenne résolution

bits 6, 7 : non utilisés

d) Diverses formes de curseur :

0 1 2 3 4 5 6 7 début : 0 début : 0 début : 6 début : 7 fin : 7 fin : 1 fin : 7 fin : 0

lignes de balayage pour la formation du curseur (carte adaptateur couleur graphique).

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 16

e) Valeurs de l'attribut pour la carte monochrome :

7 6 5 4 3 2 1 0

résultat sur l'écran fond caractère caractère normal c 0 0 0 i 1 1 1

vidéo inverse c 1 1 1 i 0 0 0 caractère souligné c 0 0 0 i 0 0 1

noir sur noir c 0 0 0 i 0 0 0 blanc sur blanc c 1 1 1 i 1 1 1

bit 3 : caractère en intensité renforcée si i = 1 bit 7 : caractère clignotant (blinking) si c = 1

f) Attribut pour la carte couleur / graphique (N / B) :

7 6 5 4 3 2 1 0 résultat sur l'écran fond caractère caractère normal c 0 0 0 i 1 1 1

vidéo inverse c 1 1 1 i 0 0 0 noir sur noir c 0 0 0 i 0 0 0

blanc sur blanc c 1 1 1 i 1 1 1

bit 3 : caractère en intensité renforcée si i = 1 bit 7 : caractère clignotant (blinking) si c = 1

g) Valeurs usuelles de l'attribut pour une carte adaptateur monochrome ou couleur : La carte CGA ne permet pas le soulignement.

valeur (hexa) carte adaptateur monochrome

carte adaptateur couleur / graphique

00H pas d'affichage 01H caractère souligné caractère normal 07H caractère normal 09H caractère souligné brillant caractère normal brillant 0FH caractère brillant 70H vidéo inverse 81H caractère souligné clignotant caractère normal clignotant 87H caractère normal clignotant F0H vidéo inverse et clignotement

Si la fonction 0x0F permet de déterminer le mode vidéo en cours d'utilisation, elle ne permet pas de déterminer quelle(s) carte(s) est (sont) présente(s).

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 17

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 18

Annexe : extraits "Programmer en Turbo Pascal" d’Armici et "Turbo-C" de Leblanc

Pour chaque interruption il existe plusieurs fonctions, référencées par la valeur contenue dans AH lors de l'appel d'interruption. La description qui suit ne constitue pas une liste exhaustive, mais présente les fonctions les plus utilisées. Pour une approche plus complète l'excellent livre de Ray Duncan, "Advanced MS-DOS", Microsoft Press, représente un outil de travail précieux. Le manuel technique de référence de votre ordinateur devrait également contenir une description détaillée concernant les interruptions.

Interruption 10H (16) : Entrées sorties vidéo Fonction : 00H choix du mode vidéo

Appel : AH = 00H AL = 00H

01H 02H 03H 04H 05H 06H 07H 0DH 0EH 0FH 10H 40H

mode texte 40 x 25, noir / blanc mode texte 40 x 25, couleur mode texte 80 x 25, noir / blanc mode texte 80 x 25, couleur mode graphique 320 x 200, 4 couleurs mode graphique 320 x 200, noir / blanc mode graphique 640 x 200 mode texte, écran monochrome mode graphique 320 x 200, 16 couleurs (EGA) mode graphique 640 x 200, 16 couleurs (EGA) mode graphique 640 x 350, noir / blanc (EGA) mode graphique 640 x 350, 4 ou 16 couleurs (EGA) mode graphique 640 x 400, noir / blanc (Olivetti)

Fonction : 01H choix de la taille du curseur

Appel : AH = 01H CH = bits 0 à 4 ligne de début du curseur CL = bits 0 à 4 ligne de fin du curseur dans la matrice de caractères

Fonction : 02H choix de la position du curseur

Appel : AH = 02H DH = numéro de la ligne (coordonnée verticale) DL = numéro de la colonne (coordonnée horizontale) BH = numéro de la page vidéo concernée (0 en mode graphique)

Fonction : 03H lecture de la position du curseur

Appel : AH = 03H BH = numéro de la page vidéo

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 19

Retour : DH = numéro de la ligne DL = numéro de la colonne CH = ligne de début du curseur CL = ligne de fin du curseur dans la matrice de caractères

Fonction 05H choix de la page vidéo active

Appel : AH = 05H AL = numéro de la page : 0 à 7

0 à 3 0 à 7 0 à 7 0 à 3 0 à 1 0 à 1

pour les modes 00H et 01H (CGA) pour les modes 02H et 03H (CGA) pour les modes 02H et 03H (EGA) pour le mode 0DH (EGA) pour le mode 0EH (EGA) pour le mode 0FH (EGA) pour le mode 10H (EGA)

Fonction 06H initialisation et défilement d'une fenêtre vers le bas

Appel : AH = 06H AL = nombre de lignes à faire défiler (0 pour toutes) BH = attribut pour la ligne effacée CH = coordonnée y du coin supérieur gauche CL = coordonnée x du coin supérieur gauche DH = coordonnée y du coin inférieur droit DL = coordonnée x du coin inférieur droit

Fonction 07H Initialisation et défilement d'une fenêtre vers le haut

Appel : AH = 07H AL = nombre de lignes à faire défiler (0 pour toutes) BH = attribut pour la ligne effacée CH = coordonnée y du coin supérieur gauche CL = coordonnée x du coin supérieur gauche DH = coordonnée y du coin inférieur droit DL = coordonnée x du coin inférieur droit

Fonction 08H lecture d'un caractère et de son attribut à la position du curseur

Appel : AH = 08H BH = numéro de la page vidéo Retour : AH = attribut du caractère AL = code ASCII du caractère lu

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 20

Fonction 09H écriture d'un caractère et de son attribut à la position du curseur

Appel : AH = 09H AL = code ASCII du caractère BH = numéro de la page vidéo BL = attribut du caractère (voir après l’exposé de la fonction 0DH) CX = facteur de répétition du caractère

Fonction 0AH écriture d'un caractère à la position du curseur

Appel : AH = 0AH AL = code ASCII du caractère BH = numéro de la page vidéo BL = couleur (seulement en mode graphique) CX = facteur de répétition du caractère

Note 1 : le caractère à écrire prend l'attribut du caractère précédemment placé à cet endroit. Note 2 : la valeur de CX détermine le nombre d'occurrences du caractère (au maximum une ligne). Fonction 0BH Sous Fonction 0 choix d'une palette de couleurs

Appel : AH = 0BH BH = 00H BL = bits 0 à 3 couleur de cadre et de fond (0 à 7) bit 4 couleur plus intense (8 à 15)

Fonction 0BH Sous Fonction 1 choix d'une palette de couleurs

Appel : AH = 0BH BH = 01H BL = bits 0 à 3 numéro de la palette de couleur :

palette 0 (vert, rouge, jaune) ou palette 1 (cyan, magenta, blanc) Fonction 0CH affichage d'un point en mode graphique

Appel : AH = 0CH AL = de 1 à 3 couleur (selon la résolution et le mode) CX = coordonnée x (horizontale) DX = coordonnée y (verticale)

Note : si le bit le plus significatif de AL vaut 1, la valeur de la nouvelle couleur est le résultat du

OU EXCLUSIF entre la valeur spécifiée et celle de la couleur du point sur l’écran. Fonction 0DH lecture d'un point en mode graphique

Appel : AH = 0DH CX = coordonnée x (horizontale) DX = coordonnée y (verticale)

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 21

Retour : AL = couleur du point (seulement en mode graphique)

fonction 0EH écriture d'un caractère et avancement du curseur

Appel : AH = 0EH AL = code ASCII du caractère BH = numéro de la page vidéo (en mode texte) BL = couleur du fond en mode graphique

Note 1 : tous les caractères ASCII sont reconnus et interprétés (CR, LF, BackSpace, ...). Note 2 : le passage à la ligne et le défilement de l'écran sont automatiques. Fonction 0FH informations sur le mode d'affichage

Appel : AH = 0FH Retour : AH = nombre de caractères par ligne AL = mode d'affichage (comme pour la fonction 00H) BH = numéro de la page active

Fonction 13H écriture d'une chaîne de caractères

Appel : AH = 13H AL =

0 1 2 3

mode d'écriture : attribut dans DL, le curseur n'est pas avancé attribut dans DL, le curseur est avancé la chaîne de caractères contient les caractères et leurs attributs (alternés), le curseur n'est pas avancé la chaîne de caractères contient les caractères et leurs attributs (alternés), le curseur est avancé

BH = numéro de la page vidéo BL = attribut (pour les modes d'écriture 0 et 1) CX = longueur de la chaîne de caractères DH = coordonnée y (ligne) de la position de la chaîne à écrire DL = coordonnée x (colonne) de la position de la chaîne à écrire ES : BP = adresse mémoire du début de la chaîne de caractères

Interruption 13H (16) : Entrées sorties disques Fonction 00H initialisation des entrées sorties disque

Appel : AH = 00H

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 22

Fonction 01H informations concernant le contrôleur de lecteur de disquettes

Appel : AH = 01H Retour : AH = état du disque et du contrôleur :

• bit 7 le disque ne répond pas • bit 6 erreur de positionnement de la tête • bit 5 erreur due au contrôleur • bit 4 erreur de donnée en lecture • bit 3 problème de DMA (accès direct en mémoire) • bit 2 secteur non trouvé • bit 1 disque protégé en écriture • bit 0 commande non valable

Fonction 02H lecture depuis un disque

Appel : AH = 02H AL = de 1 à 9 nombre de secteurs à lire ES : BX = adresse mémoire pour stocker les données lues CH = de 0 à 39 numéro de la piste CL = de 1 à 9 numéro du secteur DH = 0 ou 1 numéro de la tête DL = de 0 à 3 numéro du lecteur Retour : en cas de réussite : l'indicateur de retenue est à 0 et AH = 00H AL = nombre de secteurs transférés Retour : en cas de non réussite : l'indicateur de retenue est mis à 1 et AH = AH indique l'état (voir fonction 01H)

Fonction 03H écriture sur le disque

Appel : AH = 03H pour le reste, voir la fonction 02H

Fonction 04 vérification des secteurs d'un disque

Appel : AH = 04H pour le reste, voir la fonction 02H

Fonction 05H formatage d'une piste

Appel : AH = 05H DL = numéro du lecteur DH = numéro de la tête CH = numéro de la piste ES : BX = adresse contenant les informations de formatage d'un secteur

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 23

Interruption 16H (22) : Fonctions du clavier Fonction 00H lecture d'un caractère au clavier

Appel : AH = 00H Retour : AH = "scan code" (code attribué à la touche appuyée) AL = code ASCII du caractère

Fonction 01H informations sur l'état du clavier

Appel : AH = 01H Retour : si aucune touche n'a été appuyée : l'indicateur de détection de zéro est mis à 1 Retour : si une touche a été appuyée : l'indicateur de détection de zéro est mis à 0 AH = "scan code" de la touche AL = code ASCII du caractère

Fonction 02H indicateurs spéciaux du clavier

Appel : AH = 02H Retour : AL = indications sur des touches particulières

• bit 7 touche "Insert" • bit 6 touche "Caps Lock" • bit 5 touche "Num Lock" • bit 4 touche "Scroll Lock" • bit 3 touche "ALT" • bit 2 touche "CTRL" • bit 1 touche de majuscules de gauche • bit 0 touche de majuscules de droite

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 24

Interruption 17H (23) : Fonctions de l'imprimante Fonction 00H envoi d'un caractère à l'imprimante

Appel : AH = 00H AL = code ASCII du caractère à écrire DX = de 0 à 2 numéro de l'imprimante Retour : AH = état de l'imprimante :

• bit 7 imprimante disponible • bit 6 signal de quittance • bit 5 fin du papier • bit 4 imprimante sélectionnée • bit 3 erreur d'entrée sortie • bit 2 et 1 inutilisés • bit 0 temps d'attente dépassé

Fonction 01H initialisation du port d'imprimante (parallèle)

Appel : AH = 01H DX = de 0 à 2 numéro de l'imprimante Retour : AH = état de l'imprimante : voir fonction 00H

Fonction 02H lecture de l'état du port d'imprimante

Appel : AH = 02H DX = de 0 à 2 numéro de l'imprimante Retour : AH = état de l'imprimante : voir fonction 00H

Interruption 21H (33) : Fonctions du DOS Fonction 01H lecture d'un caractère depuis le clavier avec écho

Appel : AH = 01H Retour : AL = code ASCII du caractère

Fonction 02H affichage d'un caractère à l'écran

Appel : AH = 02H DL = code ASCII du caractère

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 25

Fonction 05H envoi d'un caractère à l'imprimante

Appel : AH = 05H DL = code ASCII du caractère

Fonction 07H lecture d’un caractère depuis le clavier sans écho

Appel : AH = 07H Retour : AL = code ASCII du caractère

Fonction 0BH informations sur l'état du clavier

Appel : AH = 0BH Retour : AL = 00H si aucune touche n'a été appuyée ou AL = FFH si une touche a été appuyée

Fonction 0EH choix du lecteur actif

Appel : AH = 0EH DL = de 0 à 255 numéro du lecteur (0 = A, 1 = B, ...) Retour : AL = nombre de lecteurs rattachés au système

Fonction 2AH obtention de la date système

Appel : AH = 2AH Retour : CX = année (1980 à 2099) DH = de 1 à 12 mois (1 à 12) DL = de 1 à 31 jour (1 à 31) AL = de 0 à 6 jour de la semaine (0 = dimanche, 1 = lundi, ..., 6 = samedi)

Fonction 2BH mise à jour de la date système

Appel : AH = 2BH CX = année (1980 à 2099) DH = de 1 à 12 mois (1 à 12) DL = de 1 à 31 jour (1 à 31) Retour : AL = 00H si la date a été fixée ou AL = FFH si la date n'est pas valable

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 26

Fonction 2CH obtention de l'heure

Appel : AH = 2CH Retour : CH = de 0 à 23 heures (0 à 23) CL = de 0 à 59 minutes (0 à 59) DH = de 0 à 59 secondes (0 à 59) DL = de 0 à 99 centièmes (0 à 99)

Fonction 2DH mise à jour de l'heure

Appel : AH = 2DH CH = de 0 à 23 heures (0 à 23) CL = de 0 à 59 minutes (0 à 59) DH = de 0 à 59 secondes (0 à 59) DL = de 0 à 99 centièmes (0 à 99) Retour : AL = 00H si l’heure a été fixée ou AL = FFH si l’heure n’est pas valable

Interruption 1AH (25) : Lecture de l’heure du système L'interruption 1AH fournit également l'heure du système. Cette fonction permet de calculer un intervalle de temps avec une précision d'environ 0.05 seconde.

Appel : AH = 00H Retour : CX = de 0 à 23 heures (0 à 23) DX = nombre de secondes multiplié par 18.206481 AL = différent de 0 si passage d'une heure à l'autre depuis la dernière

lecture

Chapitre 15 : Les Interruptions : structure du 8088 - appels du BIOS Page : 27

Exemples de fonctions long time ( long * tloc ) ; retourne (et écrit en tloc) le nombre de secondes écoulées depuis le 1-1-1970. Prototype dans la bibliothèque "time.h". char * ctime ( long * cl ) ; retourne l'adresse d'une zone (que vous n'avez pas à réservé) où le système place, en clair (mais en anglais), la date et l'heure du jour (toujours le même format, quelle que soit la date). Prototype dans la bibliothèque "time.h". Les fonctions suivantes, basées sur les appels DOS, sont disponibles en TURBO-C : long biostime ( int cmd , long nb ) Si cmd (mot de commande) vaut 0, retourne le contenu du compteur d’intervalle de temps. Celui-ci est remis à 0 à minuit et s'incrémente de 18.2 toutes les secondes. Si cmd vaut 1, ce compteur est initialisé à la valeur nb. Prototype dans la bibliothèque "bios.h". getdate ( struct date * dt ) gettime ( struct titre * tm ) setdate ( struct time * dt ) settime ( struct time * tm ) Ces 4 fonctions lisent (get) ou modifient (set) le date du système. Prototype dans la bibliothèque "dos.h". Voir les détails dans "dos.h" pour la définition des structures date et time.

Exemples de programmes

/* affichage date et heure */ # include <time.h> void main ( void ) { long nbreSec ; char * ptr ; time ( & nbreSec ) ; ptr = ctime ( & nbreSec ) ; printf ( "%s \n" , ptr ) ; * ( ptr + 19 ) = 0 ; printf ( "%s \n" , ptr + 11 ) ; }

A l’affichage sur l’écran, on obtient : Fri Jun 26 09 : 47 : 35 1987 09 : 47 : 35