Sous DOS, il existe différents timings : https://retrocomputing.stackexchange.co ... ect=1&lq=1
Le plus pratique pour l'affichage vidéo est celui du retour de balayage d'écran : https://programmersheaven.com/discussio ... in-the-vga
Il s'agit de l'interruption provoquée par beaucoup de cartes EGA ou VGA lors de ce retour. Elle signale que l'image a été rafraîchie et que la mémoire vidéo n'est plus lue. En effet, il y a plusieurs raisons de vouloir en être averti par la carte vidéo :
cadencer une animation ;
synchroniser l'écriture en mémoire vidéo avec ce laps de temps (tampon simple) ;
savoir quand permuter deux pages vidéo alors qu'on écrit dans une troisième (triple-tampon).
0 REM $LINESIZE:132
1 ' Animation dirigée au pavé numérique en mode EGA 320 x 200 16 couleurs.
2 ' Associe un masque AND à un sprite OR puis permute deux pages vidéo lors du
3 ' VBLANK, une à l'écran et une en sortie (double-tampon avec Vsync).
4 ' Requiert : - QBasic ou l'interpréteur IBM BASIC A3.20 minimum ;
5 ' - une carte vidéo compatible EGA ;
6 ' - un écran couleur.
7 ' Compilation (BC) : /E/W avec QuickBASIC 4.5 de préférence.
8 ' Edition de liens : +NOEM+SMALLERR/NOE pour réduire la taille si BC /O,
9 ' et /EX si on ne prévoit pas de compresser avec PKLITE.
100 ON ERROR GOTO 1000 ' Aller à TRAITEMENT D'ERREUR si erreur
110 PRINT "Animation graphique dirigée au clavier (touches fléchées du pavé numérique)."
120 PRINT "Appuyez sur une touche pour commencer..."
130 WHILE INKEY$ = "": WEND
200 GOSUB 2000 ' Appeler DEBUT
300 GOSUB 3000 ' Appeler BOUCLE
399 ' ----- QUITTER -----
400 ' Mode texte avec menu des touches de fonction.
410 WIDTH 80: SCREEN 0: KEY ON
420 ' Restaurer l'état du clavier.
430 DEF SEG = &H40
440 POKE &H17, PEEK(&H17) OR DRAPEAUX%
450 DEF SEG
460 ' Afficher message d'erreur s'il y a lieu.
470 PRINT MSG.ERR$
480 ' Arrêter l'interception d'erreur.
490 IF MSG.ERR$ = "" THEN CLEAR ELSE ON ERROR GOTO 0
900 END ' Ou SYSTEM pour quitter aussi l'interpréteur.
990 :
999 ' ===== T R A I T E M E N T D ' E R R E U R =====
1000 IF ERL = 31060 THEN 10000 ' Dessin hors limites
1010 ' Mode d'écran invalide
1020 IF ERL = 23000 OR ERR = 73 THEN MSG.ERR$ = "Carte vidéo non compatible EGA ou version de BASICA antérieure à la 3.20 !" ELSE MSG.ERR$ = "Erreur" + STR$(ERR) + " à la ligne" + STR$(ERL) + " : avertir l'auteur du programme."
1030 RESUME 400 ' Aller à QUITTER (on ne s'embête pas)
1990 :
1999 ' ===== D E B U T =====
2000 ' Préparer les données, l'écran et l'interception des touches du curseur.
2100 GOSUB 21000 ' Initialisation variables
2200 GOSUB 22000 ' Sauvegarde état clavier
2300 GOSUB 23000 ' Initialisation écran, base tableaux, hasard
2400 GOSUB 24000 ' Fond
2500 GOSUB 25000 ' Création sprite et masque
2600 GOSUB 26000 ' Make et break touches fléchées
2700 GOSUB 27000 ' Surveillance touches fléchées
2710 PCOPY 0, 1 ' Forcément erreur si GW-BASIC Olivetti 3.11 !
2720 PAGE.S% = 0: PAGE.E% = 1 ' Les deux pages partagent le même fond.
2900 RETURN
2990 :
2999 ' ===== B O U C L E =====
3000 ' La boucle principale est celle qui gère l'affichage.
3010 WHILE TOUCHE$ <> CHR$(27)
3100 GOSUB 31000 ' Animer dessin
3110 SWAP PAGE.S%, PAGE.E%
3120 SCREEN , , PAGE.S%, PAGE.E%
3130 WAIT &H3DA, 8 ' Synchronisation verticale pour QBasic/BASICA.
3800 TOUCHE$ = INKEY$
3810 WEND
3900 RETURN
9990 :
9997 ' -------------------
9998 ' Dessin hors limites
9999 ' -------------------
10000 IF DESSIN.X1% < 0 OR DESSIN.X1% > DESSIN.XMAX% THEN DESSIN.X1% = DESSIN.X%
10010 IF DESSIN.Y1% < 0 OR DESSIN.Y1% > DESSIN.YMAX% THEN DESSIN.Y1% = DESSIN.Y%
10900 RESUME 31060 ' Les BASIC 3.x continuent toute interception.
20900 :
20997 ' ----------------------
20998 ' Initialisation valeurs
20999 ' ----------------------
21000 ' Adressage d'écran.
21010 ECRAN.X% = 319: ECRAN.Y% = 199
21020 '
21030 ' Variables de boucle et flags.
21040 I% = 0: J% = 0:
21050 FLAG% = 0: TOUCHE$ = "": DRAPEAUX% = 0
21060 '
21070 ' Paramètres de l'animation.
21080 DESSIN.PAS.X% = 1: DESSIN.PAS.Y% = 1 ' Pixel par pixel.
21090 PAGE.S% = 0: PAGE.E% = 0 ' Pages : sortie et écran.
21100 '
21110 ' Dimensions, vecteurs, coordonnées et taille du dessin à déplacer.
21120 DESSIN.LARGEUR% = 20: DESSIN.HAUTEUR% = 20
21130 DESSIN.VX% = 0: DESSIN.VY% = 0
21140 DESSIN.X% = 0: DESSIN.Y% = 0
21150 DESSIN.X1% = DESSIN.X%: DESSIN.Y1% = DESSIN.X%
21160 DESSIN.X2% = DESSIN.X1% + DESSIN.LARGEUR% - 1
21170 DESSIN.Y2% = DESSIN.Y1% + DESSIN.HAUTEUR% - 1
21180 DESSIN.XMAX% = ECRAN.X% - DESSIN.LARGEUR% + 1
21190 DESSIN.YMAX% = ECRAN.Y% - DESSIN.HAUTEUR% + 1
21200 DESSIN.TAILLE% = (4 + INT((DESSIN.LARGEUR% + 7) / 8) * DESSIN.HAUTEUR% * 4) / 2
21210 '
21220 ' Tableaux où sont stockés le dessin, son masque et son arrière-plan.
21230 OPTION BASE 1 ' Les tableaux commencent par 1 au lieu de 0.
21240 DIM DESSIN.FOND%(DESSIN.TAILLE%)
21250 DIM DESSIN.SPRITE%(DESSIN.TAILLE%)
21260 DIM DESSIN.MASQUE%(DESSIN.TAILLE%)
21270 '
21280 ' Message d'erreur.
21290 MSG.ERR$ = ""
21900 RETURN
21910 ' **********************************************************************
21920 ' Le mode 320 x 200 16 couleurs comporte 4 plans et 4 bits par pixel.
21930 ' 4 + INT((largeur * bit par pixel par plan + 7) / 8) * hauteur * plan
21940 ' = taille du tableau en octets (un entier comporte deux octets)
21950 ' **********************************************************************
21997 ' -----------------------
21998 ' Sauvegarde état clavier
21999 ' -----------------------
22000 ' On manipule la mémoire à partir du segment 0040h.
22010 DEF SEG = &H40
22020 ' On ignore les touches Crtl, Alt ou Maj (poids faible) avant de sauver.
22030 DRAPEAUX% = PEEK(&H17) AND &HF0
22040 ' Désactiver le pavé numérique et le verrouillage majuscule (bits 5 & 6).
22050 POKE &H17, PEEK(&H17) AND &H9F
22060 ' On restitue absolument le segment BASIC par défaut.
22070 DEF SEG
22900 RETURN
22997 ' ------------------------------
22998 ' Initialisation écran et hasard
22999 ' ------------------------------
23000 SCREEN 7 ' Erreur si GW-BASIC IBM 3.21 mais pas Olivetti 3.11 !
23010 CLS : KEY OFF
23020 COLOR , 1 ' GW-BASIC 3.2x perdra la palette au prochain SCREEN !
23030 RANDOMIZE TIMER ' On initialise la liste des nombres pseudo-aléatoires
23900 RETURN ' grâce à l'horloge.
23997 ' ----
23998 ' Fond
23999 ' ----
24000 ' Ciel étoilé de confettis (16 couleurs).
24010 FOR I% = 1 TO 2000
24020 PRESET ((ECRAN.X% + 1) * RND, (ECRAN.Y% + 1) * RND), 15 * RND + 1
24030 NEXT I%
24900 RETURN
24997 '--------------------------
24998 ' Création sprite et masque
24999 '--------------------------
25000 GET (DESSIN.X1%, DESSIN.Y1%)-(DESSIN.X2%, DESSIN.Y2%), DESSIN.FOND%
25010 '
25020 FLAG% = 0 ' 0 : flag pour dessin.
25030 GOSUB 53000 ' Afficher soit dessin soit masque
25040 GET (DESSIN.X1%, DESSIN.Y1%)-(DESSIN.X2%, DESSIN.Y2%), DESSIN.SPRITE%
25050 LOCATE 25, 1 ' Erreur si Compaq GW-BASIC 3.20 !
25060 PRINT "Dessin utilisé...";
25070 WHILE INKEY$ = "": WEND
25080 '
25090 RESTORE
25100 FLAG% = NOT FLAG% ' -1 : flag pour masque.
25110 GOSUB 53000 ' Afficher soit dessin soit masque
25120 GET (DESSIN.X1%, DESSIN.Y1%)-(DESSIN.X2%, DESSIN.Y2%), DESSIN.MASQUE%
25130 LOCATE 25, 1
25140 PRINT "Masque utilisé...";
25150 WHILE INKEY$ = "": WEND
25160 '
25170 PUT (DESSIN.X1%, DESSIN.Y1%), DESSIN.FOND%, AND
25180 PUT (DESSIN.X1%, DESSIN.Y1%), DESSIN.SPRITE%, OR
25190 LOCATE 25, 1
25200 PRINT "Animation avec masque AND et sprite OR.";
25900 RETURN
25997 ' ------------------------------
25998 ' Make et break touches fléchées
25999 ' ------------------------------
26000 ' CHR$(0) signifie pavé numérique et verrouillage majuscule désactivés.
26010 ' ... + CHR$(&H80 + scancode) : code de relâchement d'une touche (BREAK).
26020 KEY 15, CHR$(0) + CHR$(128 + 72) ' GW-BASIC Olivetti 3.20 ignore BREAK !
26030 KEY 16, CHR$(0) + CHR$(128 + 75)
26040 KEY 17, CHR$(0) + CHR$(128 + 77)
26050 KEY 18, CHR$(0) + CHR$(128 + 80)
26060 ON KEY(11) GOSUB 51000 ' Aller à MAKE HAUT
26070 ON KEY(12) GOSUB 51200 ' Aller à MAKE GAUCHE
26080 ON KEY(13) GOSUB 51400 ' Aller à MAKE DROITE
26090 ON KEY(14) GOSUB 51600 ' Aller à MAKE BAS
26100 ON KEY(15) GOSUB 52000 ' Aller à BREAK HAUT
26110 ON KEY(16) GOSUB 52200 ' Aller à BREAK GAUCHE
26120 ON KEY(17) GOSUB 52400 ' Aller à BREAK DROITE
26130 ON KEY(18) GOSUB 52600 ' Aller à BREAK BAS
26900 RETURN
26997 ' -----------------------------
26998 ' Surveillance touches fléchées
26999 ' -----------------------------
27000 FOR I% = 11 TO 18
27010 KEY(I%) ON
27020 NEXT I%
27900 RETURN
30990 :
30997 ' -------------
30998 ' Animer dessin
30999 ' -------------
31000 PUT (DESSIN.X%, DESSIN.Y%), DESSIN.FOND%, PSET ' Efface avant-dernier.
31010 GET (DESSIN.X1%, DESSIN.Y1%)-(DESSIN.X2%, DESSIN.Y2%), DESSIN.FOND%
31020 DESSIN.X% = DESSIN.X1%
31030 DESSIN.Y% = DESSIN.Y1%
31040 DESSIN.X1% = DESSIN.X1% + DESSIN.VX%
31050 DESSIN.Y1% = DESSIN.Y1% + DESSIN.VY%
31060 PUT (DESSIN.X1%, DESSIN.Y1%), DESSIN.MASQUE%, AND ' Risque hors limite.
31070 PUT (DESSIN.X1%, DESSIN.Y1%), DESSIN.SPRITE%, OR
31080 DESSIN.X2% = DESSIN.X1% + DESSIN.LARGEUR% - 1
31090 DESSIN.Y2% = DESSIN.Y1% + DESSIN.HAUTEUR% - 1
31900 RETURN
49990 :
50000 ' Pas de REM (commentaires) dans les routines MAKE : pas de diagonales.
50997 ' ---------
50998 ' Make haut
50999 ' ---------
51000 REM DESSIN.VX% = 0
51010 DESSIN.VY% = -DESSIN.PAS.Y%
51090 RETURN
51197 ' -----------
51198 ' Make gauche
51199 ' -----------
51200 DESSIN.VX% = -DESSIN.PAS.X%
51210 REM DESSIN.VY% = 0
51290 RETURN
51397 ' -----------
51398 ' Make droite
51399 ' -----------
51400 DESSIN.VX% = DESSIN.PAS.X%
51410 REM DESSIN.VY% = 0
51490 RETURN
51597 ' --------
51598 ' Make bas
51599 ' --------
51600 REM DESSIN.VX% = 0
51610 DESSIN.VY% = DESSIN.PAS.Y%
51690 RETURN
51997 ' ----------
51998 ' Break haut
51999 ' ----------
52000 IF DESSIN.VY% < 0 THEN DESSIN.VY% = 0
52090 RETURN
52197 ' ------------
52198 ' Break gauche
52199 ' ------------
52200 IF DESSIN.VX% < 0 THEN DESSIN.VX% = 0
52290 RETURN
52397 ' ------------
52398 ' Break droite
52399 ' ------------
52400 IF DESSIN.VX% > 0 THEN DESSIN.VX% = 0
52490 RETURN
52597 ' ---------
52598 ' Break bas
52599 ' ---------
52600 IF DESSIN.VY% > 0 THEN DESSIN.VY% = 0
52690 RETURN
52997 ' --------------------------------
52998 ' Afficher soit dessin soit masque
52999 ' --------------------------------
53000 ' FLAG% = -1 (var. booléenne non nulle donc VRAIE) : masque sinon dessin.
53010 FOR J% = DESSIN.Y1% TO DESSIN.Y2%
53020 FOR I% = DESSIN.X1% TO DESSIN.X2%
53030 READ COULEUR%
53040 IF FLAG% THEN IF COULEUR% THEN COULEUR% = 0 ELSE COULEUR% = 15
53050 PSET (I%, J%), COULEUR%
53060 NEXT I%, J%
53900 RETURN
60990 :
60999 ' Dessin 20 x 20 pixels pour palette 16 couleurs par défaut.
61000 DATA 00,00,00,00,00,00,00,00,00,02,02,00,00,00,00,00,00,00,00,00
61010 DATA 00,00,00,00,00,00,00,00,02,02,02,02,00,00,00,00,00,00,00,00
61020 DATA 00,00,00,00,00,00,00,00,02,02,02,02,00,00,00,00,00,00,00,00
61030 DATA 00,00,00,00,00,00,00,02,02,02,02,02,02,00,00,00,00,00,00,00
61040 DATA 00,00,00,00,00,00,00,02,02,02,02,02,02,00,00,00,00,00,00,00
61050 DATA 00,00,00,00,00,00,02,02,02,04,04,02,02,02,00,00,00,00,00,00
61060 DATA 00,00,00,00,00,00,02,02,04,10,10,04,02,02,00,00,00,00,00,00
61070 DATA 00,00,00,00,00,00,02,02,02,02,02,02,02,02,00,00,00,00,00,00
61080 DATA 00,00,00,00,00,00,00,02,02,06,06,02,02,00,00,00,00,00,00,00
61090 DATA 00,00,00,00,00,00,00,00,02,02,02,02,00,00,00,00,00,00,00,00
61100 DATA 00,00,00,00,00,00,00,00,00,02,02,00,00,00,00,00,00,00,00,00
61110 DATA 00,00,00,00,00,00,00,00,05,05,05,05,00,00,00,00,00,00,00,00
61120 DATA 00,00,00,00,11,11,11,07,07,07,07,07,07,11,11,11,00,00,00,00
61130 DATA 00,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,00
61140 DATA 07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07,07
61150 DATA 08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08
61160 DATA 00,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,08,00
61170 DATA 00,00,00,00,08,08,08,08,08,08,08,08,08,08,08,08,00,00,00,00
61180 DATA 00,00,00,00,00,00,00,00,08,08,08,08,00,00,00,00,00,00,00,00
61190 DATA 00,00,00,00,00,00,00,00,00,08,08,00,00,00,00,00,00,00,00,00
61200 ' IBM BASICA 3.40 cherche les 5 1ers Ko du BASIC en ROM via INT15h Fct22h.
Il s'agit d'une démonstration du double-tampon sous BASICA (EGA 64 Ko). Elle tourne plus rapidement sous QBasic mais garde un gros inconvénient : elle ne fait strictement rien pendant l'attente due à l'instruction WAIT &H3DA,8 (ligne 3130). Ce temps perdu pourrait avancer l'écriture du second tampon. L'utilisation du timer PC est possible mais reste inférieure à celle de l'interruption existante sur la plupart des cartes EGA et VGA.
La carte EGA d'IBM utilisait l'IRQ2. J'avais corrigé le programme d'exemple proposé ici pour le mode MS-DOS (inopérant dans une fenêtre DOS) : viewtopic.php?f=16&t=26369
De nos jours, c'est le BIOS qui choisit pour nos cartes VGA. C'est pourquoi j'ai ajouté une ligne IRQ au début du fichier en assembleur : l'utilisateur n'a qu'à changer avec le numéro attribué à sa carte. Le programme appelant est en C. Problème : nous autres du peuple préférons l'EDI de QuickBASIC. Il va donc falloir imiter 12-2.C et adapter 12-1.ASM que nous pouvons visualiser au lien précédent.
Explications
L'interface entre le C Microsoft et l'assembleur est très souple. L'environnement QuickBASIC semble plus fermé, le BASIC ne partageant pas facilement ses procédures et données. Cependant, depuis la version 4.0b, il est permis à une routine externe de demander une action au BASIC. Il s'agit de la scrutation UEVENT : http://jeffpar.github.io/kbarchive/kb/033/Q33488/
Il suffit alors que la routine appelle la procédure lointaine SetUEvent au moment venu. Si l'application doit être autonome, c'est-à-dire, sans nécessiter le run-time après compilation, la routine doit être liée au segment nommé CODE. Il est bon de savoir que cet appel modifie les registres AX et BX car un handler d'interruption (ISR) doit préserver tous les registres.
Pour l'exemple, nous utiliserons QuickBASIC 4.5, comportant la bibliothèque BCOM45, le run-time BRUN45 (.LIB et .EXE) et la QuickLibrary BQLB45. Nous avons vu que le programmeur C sauvait les registres BP, SI et DI à l'entrée de toute routine appelé par le programme C, puis les restituait à la sortie. De même pour le segment de données DS lorsqu'il était modifié. Et bien, le programmeur QuickBASIC doit aussi se soucier du segment extra ES. En outre, l'emploi d'un assembleur compatible MASM est requis (autre que la version IBM 1.0).
Nous savons que les procédures de BASIC n'ont pas de code de retour, alors que C emploie le registre AX pour cela. Nous enverrons donc une variable entière comme paramètre à notre routine d'initialisation. Elle renverra ainsi son succès ou son échec :
D'ailleurs, les procédures externes doivent être appelées par leur adresse complète. Elles seront donc déclarées FAR en plus de PUBLIC.
Pour tester nos routines dans l'environnement QuickBASIC, nous aurons à les incorporer dans une QuickLib. Supposant que notre fichier source s'appelle VREGA.ASM, nous emploierons la QuickLib par défaut et le paramètre QUICKLIBRARY de LINK :
Une fois les tests concluants, nous passerons à la compilation. Vu que notre programme ne comporte pas de numéro de ligne, la scrutation d'événement se fera par la paramètre V et non W. De plus, si on souhaite ne pas dépendre de BRUN45.EXE et que notre routine appelant SetUEvent se trouve dans le segment CODE, nous compilerons une application EXE autonome (/O) :
Dernière chose, Andrew Schulman dans les Coulisses du DOS "Undocumented DOS", chapitre 2, explique qu'une application compilée par QB45 ne peut pas renvoyer un code de retour à la ligne de commande (ERRORLEVEL). L'astuce d'appeler directement la fonction 4Ch de l'interruption 21h du DOS court-circuite le BASIC, empêchant ce dernier de restituer le curseur. Plus tard, on a su que la QuickLibrary par défaut comportait le module C _exit fonctionnant très bien à une condition : que l'application EXE ait été autonome. Attention toutefois à son emploi dans l'environnement QuickBASIC (QB /L), cela équivaudrait à exécuter l'instruction SYSTEM (quitter l'interpréteur). Exemple :
DECLARE SUB Bye ALIAS "_exit" (BYVAL ErrorLevel%)
ON UEVENT GOSUB Increment
UEVENT ON
CALL EnableISR0A(VRcount%)
IF VRcount% THEN
PRINT "Impossible d'activer le handler d'interruptions verticales"
Bye 1
END IF
WHILE VRcount% < 600
PRINT "Nombre d'interruptions verticales :"; STR$(VRcount%)
PRINT CHR$(30);
WEND
UEVENT OFF
CALL DisableISR0A
END
Increment:
LET VRcount% = VRcount% + 1
RETURN
VREGA.ASM à assembler pour lier à TEST.OBJ ou afin de créer la QuickLibrary VREGA.QLB :
TITLE 'Listing 12.1'
NAME VREGA
PAGE 55,132
;
; Nom : VREGA
;
; Fonction : Routine d'interruption verticale pour EGA et VGA
;
; Appelant : QuickBASIC 4.0b
;
; EnableISR0A (integer%) 'retourne 0 si bien installée
;
; DisableISR0A
;
; QuickLib : LINK VREGA,,,BQLB??/Q;
;
; Une carte VGA PnP peut se voir attribuer une ligne IRQ autre que 2/9 :
IRQ EQU 2 ; le PC/AT redirige la ligne IRQ2 sur 9.
if IRQ lt 2 or IRQ eq 6 or IRQ eq 8 or IRQ eq 13 ; PIT/KBD/FDC/RTC/FPU
if1
%out Ligne IRQ réservée !
%out Assemblage interromptu.
endif
else
CRT_MODE EQU 49h ; adresses de la zone des informations
ADDR_6845 EQU 63h ; vidéo du BIOS
DGROUP GROUP _DATA
CODE SEGMENT byte public 'CODE' ; seg BASIC requis pour BC /O
ASSUME cs:CODE,ds:DGROUP
EXTRN Setuevent:far ; UEVENT dans le pgm QB qui appelle
ISR0A PROC far ; handler d'interruption pour l'INT 0Ah
push ax ; préservation registres
push bx
push dx
push ds
mov ax,seg DGROUP
mov ds,ax ; DS -> DGROUP
if IRQ ge 8 and IRQ ne 9
mov al,20h
out 0A0h,al ; EOI au PIC esclave du PC/AT
endif
; Voir si c'est bien une interruption verticale
mov dx,3C2h ; DX := port d'E/S pour le
; registre 0 d'état des entrées
in al,dx
test al,80h ; tester le bit 7 du reg. d'état
jnz L10 ; saut si interruption verticale
; Ce n'est pas une interruption verticale. On chaîne donc le traitement
; vers l'ancien handler
if IRQ lt 8 and IRQ ne 2
mov al,20h
out 20h,al ; EOI au PIC maître du PC/AT
endif
pushf ; simuler une INT
call ds:PrevISR0A ; vers l'ancien handler de l'Int 0Ah
jmp short Lexit
; Traiter une interruption verticale
L10: mov dx,Port3x4 ; DX := 3B4h or 3D4h
in al,dx ; AL := valeur du reg. adresse du CRTC
push ax ; la sauvegarder
mov ax,DefaultVREnd ; AH := valeur par défaut pour reg. VR End
; AL := 11h (numéro du registre)
and ah,11101111b ; AH bit 4 := 0 (RAZ latch d'interruption)
out dx,ax ; modifier le registre VR End
jmp $+2 ; laisser un peu de temps au CRTC
sti ; activer les interruptions
; Faire quelque chose d'utile...
call Setuevent ; attention : modifie AX et BX
; Envoyer un signal de fin d'interruption au PIC Intel 8259A pour
; autoriser les interruptions sur IRQ2 suivantes
cli ; désactiver les interruptions
mov al,20h ; port du 8259A
out 20h,al ; envoyer un EOI non spécifique au 8259A
jmp $+2 ; attendre sa réponse
; Permettre au CRTC de produire d'autres interruptions
mov ax,DefaultVREnd ; AH := valeur par défaut pour le reg. VR End
; AL := 11h (numéro du registre)
and ah,11011111b ; AH bit 5 := 0 (autoriser int. verticale)
or ah,00010000b ; AH bit 4 := 1 (activer le latch
out dx,ax ; d'interruption)
jmp $+2
pop ax
out dx,al ; restaurer précédente adresse registre
Lexit: pop ds ; restauration registres et retour
pop dx
pop bx
pop ax
iret
ISR0A ENDP
;
; EnableISR0A -- Activer un handler d'Interruption
;
PUBLIC EnableISR0A
EnableISR0A PROC far
push bp ; préservation registres
mov bp,sp
push es
mov ax,40h
mov es,ax ; ES -> zone des infos vidéo du BIOS
; Sauver les valeurs des registres du CRTC
mov dx,es:[ADDR_6845] ; DX := adresse du port CRTC
mov Port3x4,dx ; sauvegarder l'adresse
mov ax,1A00h ; AH := 1AH (numéro fonction INT 10H)
; AL := 0 (lire combinaison d'affichage)
int 10h ; AL := 1AH si fonction 1AH supportée
; BL := sous-système vidéo actif
cmp al,1Ah
jne L20 ; saut si pas VGA
cmp bl,7
je L21 ; saut si VGA
cmp bl,8
je L21 ; saut si VGA
mov ax,0FFFFh ; renvoyer 0FFFFh si ni EGA ni VGA
jmp short L23
; Obtenir la valeur par défaut pour le registre EGA Vertical Retrace End
L20: mov al,es:[CRT_MODE] ; AL := numéro du mode video du BIOS
mov bx,offset DGROUP:EGADefaultVals
xlat ; AL := valeur par défaut pour le reg. VR End
jmp short L22
; Obtenir la valeur par défaut pour le registre VGA Vertical Retrace End
L21: mov al,VREndReg ; AL := numéro du registre VR End
out dx,al
inc dx ; DX := 3B5H or 3D5H
in al,dx ; AL := valeur courante de ce registre
L22: mov VREndValue,al ; la sauvegarder
; Sauvegarder l'ancien vecteur d'interruption 0Ah
if IRQ eq 9
mov ax,350Ah ; AH := 35H (numéro de la fonction INT 21h)
; AL := 0AH (numéro de l'interruption)
else
if IRQ ge 8
mov ax,3570h+IRQ-8
else
mov ax,3508h+IRQ
endif
endif
int 21h ; ES:BX := précédent vecteur de l'Int 0AH
mov word ptr PrevISR0A,bx
mov word ptr PrevISR0A+2,es ; le sauvegarder
; Modifier le vecteur d'interruption avec l'adresse du présent handler
push ds ; préserver DS
mov dx,offset ISR0A
push cs
pop ds ; DS:DX -> ISR0A
if IRQ eq 9
mov ax,250Ah ; AH := 25H (numéro de fonction de l'Int 21H)
; AL := 0AH (numéro interruption)
else
if IRQ ge 8
mov ax,2570h+IRQ-8
else
mov ax,2508h+IRQ
endif
endif
int 21h ; modifier le vecteur d'interruption Int 0AH
pop ds ; restaurer DS
; Réactiver IRQ2 en mettant à 0 le bit 2 du registre de masque du 8259A
cli ; verrouiller les interruptions
if IRQ eq 9
mov dx,21h ; DX := registre de masque du 8259A
in al,dx ; AL := valeur présente
and al,11111011b ; RAZ bit 2
else
if IRQ ge 8
mov dx,0A1h
in al,dx
and al,not(1 shl (IRQ-8))
else
mov dx,21h
in al,dx
and al,not(1 shl IRQ)
endif
endif
out dx,al
; Réactiver les interruptions verticales
mov dx,Port3x4 ; DX := 3B4H or 3D4H
mov ax,DefaultVREnd
and ah,11001111b
out dx,ax ; RAZ bits 4 et 5 du registre VR End
jmp $+2 ; laisser le temps au CRTC
or ah,00010000b
out dx,ax ; mise à 1 du bit 4
jmp $+2
sti ; réactiver les interruptions
xor ax,ax ; AX := 0 (valeur de retour)
L23: mov bx,[bp+6]
mov [bx],ax
pop es ; restauration registres et retour
mov sp,bp
pop bp
ret 2
EnableISR0A ENDP
;
; DisableISR0A -- Désactiver le handler d'interruptions verticales
;
PUBLIC DisableISR0A
DisableISR0A PROC far
push bp
mov bp,sp
push ds
; Désactiver les interruptions verticales
cli ; verrouiller les interruptions
mov dx,Port3x4
mov ax,DefaultVREnd
out dx,ax ; restaurer le reg. Vertical Retrace End
jmp $+2
sti ; réactiver les interruptions
; Restaurer le précédent handler du vecteur d'interruption 0Ah
lds dx,PrevISR0A ; DS:DX := précédent vecteur Int 0AH
if IRQ eq 9
mov ax,250Ah ; AH := 25H (numéro fonction INT 21H)
; AL := 0AH (numéro interruption)
else
if IRQ ge 8
mov ax,2570h+IRQ-8
else
mov ax,2508h+IRQ
endif
endif
int 21h
pop ds ; restauration registres et retour
mov sp,bp
pop bp
ret
DisableISR0A ENDP
CODE ENDS
_DATA SEGMENT word public 'DATA'
PrevISR0A DD ? ; zone de sauvegarde pour l'ancien
; vecteur d'interruption 0Ah
Port3x4 DW ? ; 3B4h ou 3D4h
DefaultVREnd LABEL word
VREndReg DB 11h ; numéro registre Vertical Retrace End
VREndValue DB ? ; valeur par défaut pour reg.VR End
EGADefaultVals DB 2Bh,2Bh,2Bh,2Bh,24h,24h,23h,2Eh ; valeur par défaut
DB 00h,00h,00h,00h,00h,24h,23h,2Eh ; pour registre EGA
DB 2Bh ; EGA VR End
_DATA ENDS
endif
END
Le numéro IRQ doit correspondre à celui de la carte vidéo du programmeur.
Conclusion
Ce que nous avons appris :
retourner un ERRORLEVEL avec une application compilée par QB 4.5 et non PDS 7.0 ;
créer une QuickLib pour tester une routine assembleur à partir de l'EDI BASIC ;
répondre à un appel externe en permettant à UEVENT de scruter ;
traiter une interruption EGA/VGA lorsqu'elle se produit.
P.S. : pour certaines cartes compatibles EGA, JNZ L10 serait à remplacer par JZ L10.
Michael Abrash et Dan Illowsky sont plus précis sur ce point (polarité du bit 7 du registre Input Status 0) : https://archive.org/stream/PC_Tech_Jour ... 3/mode/2up
Dernière modification par gm86 le 08 avr. 2020 19:28, modifié 2 fois.
Il suffit que la puce VGA soit compatible, voire qu'un jumper ou une option du BIOS le lui permette (Assign IRQ For Vga dans le SETUP).
Il paraît que le bit 7 (port 3C2h au début de l'ISR) possède une polarité différente sur certains clones EGA. Ce qui est gênant vu qu'on traiterait alors l'interruption d'un périphérique autre que la carte vidéo.
L'identification de la source de l'interruption est différente sur la carte MCGA. On peut le voir au début du listing 12-3 de ma disquette. Néanmoins, à l'instar du listing EGA/VGA original, l'IRQ2 reçoit une EOI avant de « faire quelque chose de très utile » entre les instructions STI et CLI. Or je ne sais pas si cela suffit à faire planter un PS/2. Je laisse donc le soin d'une éventuelle correction au possesseur d'un modèle 25 ou 30.
Quant à la procédure d'une carte XGA, il faut se référer au PC Graphics Handbook (§14.4.5 The Vertical Retrace Interrupt, XGA Screen Blanking Interrupt).