; Illustre la division d'écran du VGA/EGA. ; Assemblé avec TASM 4.0, lié avec TLINK 6.10 ; Testé par Jim Mischel 11/21/94 ; ;********************************************************************* IS_VGA equ 1 ;0=EGA ; VGA_SEGMENT equ 0a000h SCREEN_WIDTH equ 640 SCREEN_HEIGHT equ 350 CRTC_INDEX equ 3d4h ;registre Index Controller CRT OVERFLOW equ 7 ;index du registre Overflow dans ;CRTC MAXIMUM_SCAN_LINE equ 9 ;index du registre Maximum Scan Line ;dans CRTC START_ADDRESS_HIGH equ 0ch ;index du registre Start Address High ;dans CRTC START_ADDRESS_LOW equ 0dh ;index du registre Start Address Low ;dans CRTC LINE_COMPARE equ 18h ;index du registre Line Compare ;(bits 7-0 du début de la ligne ;d'affichage de la division d'écran) ;dans CRTC INPUT_STATUS_0 equ 3dah ;registre Input Status 0 WORD_OUTS_OK equ 1 ;paramètre 0 pour assembler les pour ; ordinateurs qui ne peuvent pas traiter ;les outs 1 6bits dans les registres indexés VGA ;********************************************************************* ; Macro pour diriger une valeur de mot sur un port. ; OUT_WORD macro if WORD_OUTS_OK out dx,ax else out dx,al inc dx xchg ah,al out dx,al dec dx xchg ah,al endif endm ;********************************************************************* MyStack segment para stack 'STACK' db 512 dup (0) MyStack ends ;********************************************************************* Data segment SplitScreenLine dw ? ;ligne après laquelle commence ;la division d'écran StartAddress dw ? ;offset de la mémoire vidéo ou ;la recherche des données commence ; Message affichée dans la division d'écran. SplitScreenMsg db 'Split screen text row #' DigitInsert dw ? db '...$' Data ends ;********************************************************************* Code segment assume cs:Code, ds:Data ;********************************************************************* Start proc near mov ax,Data mov ds,ax ; ; Sélectionne le mode 10h, mode graphique 640x350 en 16 couleurs. ; mov ax,0010h ;AH=0 fonction de sélection du mode ;AL=10h mode à sélectionner, ;mode graphique 640x350 en 16 couleurs int 10h ; ; Met le texte dans la mémoire vidéo commençant à l'offset 0, avec chaque rangée ; numéroté. Cette partie de la mémoire sera affichée ; dans la partie de l'écran divisé ; mov cx,25 ;nombre de lignes de texte que nous ;afficherons dans la partie de la ;mémoire de la division d'écran FillSplitScreenLoop: mov ah,2 ;fonction positionnement du curseur sub bh,bh ;paramètre le curseur en page 0 mov dh,25 sub dh,cl ;calcule la rangée à afficher au sub dl,dl ;début de la colonne 0 int 10h ;paramètre l'emplacement du curseur mov al,25 sub al,cl ;recalcule la rangée à afficher sub ah,ah ;met la valeur en mot pour la division mov dh,10 div dh ;divise le numéro de rangée en deux ;chiffres add ax,'00' ;convertit les chiffres en ASCII mov [DigitInsert],ax ;met les chiffres dans le ; texte à afficher mov ah,9 mov dx,offset SplitScreenMsg int 21h ;affiche le texte loop FillSplitScreenLoop ; ; Remplit la mémoire vidéo commençant en 8000h avec un motif rayé ; en diagonale. ; mov ax,VGA_SEGMENT mov es,ax mov di,8000h mov dx,SCREEN_HEIGHT ;remplit toutes les lignes mov ax,8888h ;motif de remplissage de départ cld RowLoop: mov cx,SCREEN_WIDTH/8/2 ;remplit une ligne ;d'affichage un mot à la fois rep stosw ;remplit la ligne d'affichage ror ax,1 ;décale le mot du motif dec dx jnz RowLoop ; ; Paramètre l'adresse de départ en 8000h et affiche cette partie de la mémoire. ; mov [StartAddress],8000h call SetStartAddress ; ; Déplace la division d'écran à mi-chemin du haut de l'écran puis la descend ; d'un quart de l'écran. ; mov [SplitScreenLine],SCREEN_HEIGHT-1 ;paramètre la ligne initiale juste ;en bas de l'écran mov cx,SCREEN_HEIGHT/2 call SplitScreenUp mov cx,SCREEN_HEIGHT/4 call SplitScreenDown ; ; Déplace maintenant d'une autre moitié en haut de l'écran puis descend d'un quart; mov cx,SCREEN_HEIGHT/2 call SplitScreenUp mov cx,SCREEN_HEIGHT/4 call SplitScreenDown ; ; Enfin le déplace en haut de l'écran. ; mov cx,SCREEN_HEIGHT/2-2 call SplitScreenUp ; ; Attend une touche clavier (il n'y a pas d'écho). ; mov ah,8 ;fonction d'entrée console sans écho ;de DOS int 21h ; ; Désactive la division d'écran. ; mov [SplitScreenLine],0ffffh call SetSplitScreenScanLine ; ; Attend une touche clavier (il n'y a pas d'écho). ; mov ah,8 ;fonction d'entrée console sans écho ;de DOS int 21h ; mov [StartAddress],0 call SetStartAddress ; ; Passe de la division d'écran à l'écran normal (et vice versa) à chaque dixième ; trame jusqu'à ce qu'une touche clavier soit pressée. ; FlipLoop: xor [SplitScreenLine],0ffffh call SetSplitScreenScanLine mov cx,10 CountVerticalSyncsLoop: call WaitForVerticalSyncEnd loop CountVerticalSyncsLoop mov ah,0bh ;statut du caractère disponible ;de DOS int 21h and al,al ;caractère disponible? jz FlipLoop ;non, sélectionner/désélectionner ;la division d'écran mov ah,1 int 21h ;efface le caractère ; ; Revient en mode texte et à DOS. ; mov ax,0003h ;AH=0 fonction de sélection du mode ;AL=3 mode à sélectionner, mode texte int 10h ;revient en mode texte mov ah,4ch int 21h ;revient à DOS Start endp ;********************************************************************* ; Attend le front montant du signal de synchronisation vertical ; ; Entrée: aucune ; ; Sortie: aucune ; ; Registres altérés: AL, DX ; WaitForVerticalSyncStart proc near mov dx,INPUT_STATUS_0 WaitNotVerticalSync: in al,dx test al,08h jnz WaitNotVerticalSync WaitVerticalSync: in al,dx test al,08h jz WaitVerticalSync ret WaitForVerticalSyncStart endp ;********************************************************************* ; Attend le SSV ; ; Entrée: aucune ; ; Sortie: aucune ; ; Registres altérés: AL, DX ; WaitForVerticalSyncEnd proc near mov dx,INPUT_STATUS_0 WaitVerticalSync2: in al,dx test al,08h jz WaitVerticalSync2 WaitNotVerticalSync2: in al,dx test al,08h jnz WaitNotVerticalSync2 ret WaitForVerticalSyncEnd endp ;********************************************************************* ; Paramètre l'adresse de départ à la valeur spécifiée par StartAddress. ; attend le SSV avant de paramétrer si bien qu'une moitié de l'adresse ; est chargée avant le début de la trame ; et l'autre moitié après, La nouvelle adresse de départ ne sera pas ; chargée jusqu'au début de la prochaine trame; c'est-à-dire,qu'une trame ; sera affichée avant que la nouvelle adresse de départ prenne effet. ; ; Entrée: aucune ; ; Sortie: aucune ; ; Registres altérés: AX, DX ; SetStartAddress proc near call WaitForVerticalSyncEnd mov dx,CRTC_INDEX mov al,START_ADDRESS_HIGH mov ah,byte ptr [StartAddress+1] cli ;s'assure que les deux registres sont ; positionnés en une seule fois OUT_WORD mov al,START_ADDRESS_LOW mov ah,byte ptr [StartAddress] OUT_WORD sti ret SetStartAddress endp ;********************************************************************* ; Paramètre la ligne d'affichage après laquelle la division d'écran commence ; La ligne d'affichage est spécifiée par SplitScreenLine ; ; Entrée: aucune ; ; Sortie: aucune ; ; Tous les registres sont préservés ; SetSplitScreenScanLine proc near push ax push cx push dx ; ; Attend le SSV. Ce qui nous assure de ne pas récupéré une mauvaise ; partie du positionnement de la division d'écran ; call WaitForVerticalSyncStart ; ; Paramètre la ligne d'affichage de la division d'écran. ; mov dx,CRTC_INDEX mov ah,byte ptr [SplitScreenLine] mov al,LINE_COMPARE cli ;assure que tous les registres sont ;positionnés en une seule fois OUT_WORD ;paramètre les bits 7-0 de la ligne ;d'affichage de la division d'écran mov ah,byte ptr [SplitScreenLine+1] and ah,1 mov cl,4 shl ah,cl ;met le bit 8 de la ligne ;d'affichage de la division d'écran ;en position pour le registre Overflow mov al,OVERFLOW if IS_VGA ; ; Les registres Split Screen, Overflow, et Line Compare contiennent tous ; une partie de la ligne d'affichage de départ de la division d'écran de VGA. ; Nous bénéficierons des registres lisibles du VGA pour laisser tels quels ; les autres bits des registres auxquels nous accédons. ; out dx,al ;positionne le registre Index CRTC ;pour pointer sur Overflow inc dx ;pointe sur le registre Data CRTC in al,dx ;récupère le paramétrage du registre ;Overflow and al,not 10h ;désactive le bit 8 de la division ;d'écran or al,ah ;insère le nouveau bit 8 de la ;division d'écran (fonctionne en tout ;mode) out dx,al ;paramètre le nouveau bit 8 de la ;division d'écran dec dx ;sur le registre Index CRTC mov ah,byte ptr [SplitScreenLine+1] and ah,2 mov cl,3 ror ah,cl ;met le bit 9 de la ligne d'affichage ;de la division d'écran. mov al,MAXIMUM_SCAN_LINE out dx,al ;paramètre le registre Index CRTC ;pour pointer sur Maximum Scan Line inc dx ;pointe sur le registre Data CRTC in al,dx ;récupère le paramétrage courant de ;Maximum Scan Line and al,not 40h ;désactive le bit 9 de la ;division d'écran or al,ah ;insère le nouveau bit 9 de la ;division d'écran (fonctionne en tout ;mode) out dx,al ;paramètre le nouveau bit 9 de la ;division d'écran else ; ; Seuls les registres Split Screen et Overflow contiennent une partie ; de la ligne d'affichage de départ de Split Screen et doivent être positionnés ; sur EGA.Les registres EGA ne sont pas lisibles, nous devons donc paramétrer ; les bits du registre Overflow qui ne sont pas à une valeur preset dans la ;division d'écran, dans ce cas la valeur est pour les modes 350 lignes d'affichage ; or ah,0fh ;insère le nouveau bit 8 de la ;division d'écran (fonctionne ;seulement dans les modes 350 lignes ;d'affichage d'EGA) OUT_WORD ;paramètre le nouveau bit 8 de la ;division d'écran endif sti pop dx pop cx pop ax ret SetSplitScreenScanLine endp ;********************************************************************* ; Déplace vers le haut la division d'écran en fonction du nombre de lignes ; d'affichage spécifié. ; ; Entrée: CX = nombre de lignes d'affichage selon lequel la division d'écran est ; déplacée vers le haut ; ; Sortie: aucune ; ; Registre altéré: CX ; SplitScreenUp proc near SplitScreenUpLoop: dec [SplitScreenLine] call SetSplitScreenScanLine loop SplitScreenUpLoop ret SplitScreenUp endp ;********************************************************************* ; Déplace vers le bas la division d'écran en fonction du nombre de lignes ; d'affichage spécifié. ; ; Entrée: CX = nombre de lignes d'affichage selon lequel la division d'écran est ;déplacée vers le bas ; ; Sortie: aucune ; ; Registre altéré: CX ; SplitScreenDown proc near SplitScreenDownLoop: inc [SplitScreenLine] call SetSplitScreenScanLine loop SplitScreenDownLoop ret SplitScreenDown endp ;********************************************************************* Code ends end Start