Win3x.Org

Windows & DOS Community

Implémenter une gestion d'interruption EGA/VGA en QuickBASIC

Répondre   Page 1 sur 1  [ 5 messages ]
Auteur Message
gm86
Sujet du message : Implémenter une gestion d'interruption EGA/VGA en QuickBASIC
Publié : 07 avr. 2020 15:32
Membre inscrit
Avatar de l’utilisateur
Hors-ligne
 
Messages : 630
Inscription : 01 sept. 2008 19:07
 
Introduction

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).
Ce sujet est revenu sur le Web récemment :
http://www.vcfed.org/forum/showthread.p ... on-EGA-VGA
http://www.vogons.org/viewtopic.php?t=58445
Hier, j'ai décidé de m'y remettre.

Voici un programme d'animation vidéo déjà présenté sur ce forum, il y a longtemps :
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 :
L23:	mov	bx,[bp+6]
	mov	[bx],ax
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 :
MASM VREGA;
LINK VREGA,,,BQLB45/Q;
Supposant que notre programme BASIC s'appelle TEST.BAS, nous l'exécuterons ainsi :
QB TEST /L VREGA
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) :
MASM VREGA;
BC TEST/V/O;
LINK TEST+VREGA;
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%)
REM (...)
Bye Level%	' ERRORLEVEL = Level% AND 255

Fichiers

Exemple TEST.BAS à compiler puis à lier avec VREGA.OBJ pour obtenir l'exécutable TEST.EXE :
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.

_________________

C:\ONGRTLNS.W95


Haut
Profil Citer
Deksor
Sujet du message : Re: Implémenter une gestion d'interruption EGA/VGA en QuickBASIC
Publié : 07 avr. 2020 19:12
Modérateur
Avatar de l’utilisateur
En ligne
 
Messages : 4873
Inscription : 23 mai 2011 13:33
PC Rétro : 486DX 33 8mo SB Pro 2, P2 450 128mo Voodoo 2 SLI
 
Intéressant tout ça !
Je ne savais même pas qu'on pouvait faire ça avec une carte VGA peu importe le langage :lol:

C'est très très pratique !!

_________________

Laptop hater


Haut
Profil Citer
gm86
Sujet du message : Re: Implémenter une gestion d'interruption EGA/VGA en QuickBASIC
Publié : 07 avr. 2020 21:25
Membre inscrit
Avatar de l’utilisateur
Hors-ligne
 
Messages : 630
Inscription : 01 sept. 2008 19:07
 
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).

La programmation vidéo était aussi un prétexte pour présenter SetUEvent donc voici la documentation en ligne de Microsoft :
Q31936: Example of Using SetUEvent, ON UEVENT to Detect Mouse Presence
Q32164: BASIC Example of CALL SetUEvent, ON UEVENT GOSUB Trapping
Q35968: CALL SetUEvent in Assembly Routine to Disable PRINT SCREEN
Q46182: ON TIMER Can Wrongly Trigger CALL SETUEVENT, ON UEVENT Trap
Q69159: Example of How to Call BASIC SetUEvent from C; ON UEVENT GOSUB

Sans oublier l'ERRORLEVEL de la QuickLibary standard non documenté par Microsoft lui-même ! La preuve :
Q41533: BASIC 7.00 Can Return Exit Code (Error Level) to Batch File

_________________

C:\ONGRTLNS.W95


Haut
Profil Citer
redstone02
Sujet du message : Re: Implémenter une gestion d'interruption EGA/VGA en QuickBASIC
Publié : 26 avr. 2020 20:06
Membre inscrit
Avatar de l’utilisateur
Hors-ligne
 
Messages : 122
Inscription : 21 déc. 2018 18:08
PC Rétro : coming soon
 
mais c'est simple changer erorlevel
shell "set erorlevel=1"

_________________

C:\>win
:tada:win 3.11:tada:


Haut
Profil Citer
gm86
Sujet du message : Re: Implémenter une gestion d'interruption EGA/VGA en QuickBASIC
Publié : 26 avr. 2020 21:12
Membre inscrit
Avatar de l’utilisateur
Hors-ligne
 
Messages : 630
Inscription : 01 sept. 2008 19:07
 
C'est une astuce pour fichier batch qui a l'inconvénient de créer une variable ne revenant pas automatiquement à zéro.

_________________

C:\ONGRTLNS.W95


Haut
Profil
Afficher : Trier par : Ordre :
Répondre   Page 1 sur 1  [ 5 messages ]
Revenir à « Projets aboutis »
Aller :