; *** Listing 3-1 *** ; ; Le timer Zen de précision (PZTIMER.ASM) ; ; Utilise le timer du 8253 pour mesurer les performances d'un code qui ; s'exécute en moins de 54 millisecondes, avec une résolution ; supérieure à 10 microsecondes. ; ; Par Michael Abrash ; ; Routines externes appelables : ; ; ZTimerOn: démarre le timer Zen, avec les interruptions désactivées. ; ; ZTimerOff: arrête le timer Zen, sauve le compteur, ; mesure le surplus de code, et réactive les interruptions dans l'état ; où elles étaient au moment de l'appel à ZTimerOn. ; ; ZTimerReport: Affiche le temps exact écoulé entre le démarrage et ; et l'arrêt du timer. ; ; Note: Si plus de 54 ms se passent entre les appels à ZTimerOn et à ; ZTimerOff, le timer déborde et la mesure est ; inexacte. Dans ce cas, un message d'erreur est affiché ; à la place du résultat. Le timer Zen de longue période devrait être alors ; utilisé. ; ; Note: Les interruptions *DOIVENT* être désactivées entre les appels à ZTimerOn ; et à ZTimerOff pour une mesure précise et pour détecter le débordement. ; ; Note: Ces routines peuvent introduire de petites inexactitudes dans ; l'horloge système à chaque section de code mesurée, même si le ; timer 0 ne déborde pas. Si timer 0 déborde, l'horloge ; système peut ralentir un certain temps, car elle ne progresse pas ; pendant la mesure du timer de précision. Par conséquent, il est conseillé ; de relancer l'ordinateur après chaque session de mesure. ; ; Tous les registres et drapeaux, excepté le drapeau des interruptions, sont ; préservées par toutes les routines. Les interruptions sont désactivées puis ; réactivées par ZTimerOn, et sont restaurées par ZTimerOff dans l'état où elles ; étaient avant l'appel à ZTimerOn. ; Code segment word public 'CODE' assume cs:Code, ds:nothing public ZTimerOn, ZTimerOff, ZTimerReport ; Adresse de base du timer 8253. BASE_8253 equ 40h ; ; Adresse du registre compteur du timer 0 du 8253. ; TIMER_0_8253 equ BASE_8253 + 0 ; ; Adresse du registre mode du 8253. ; MODE_8253 equ BASE_8253 + 3 ; ; Adresse de Operation Command Word 3 du Programmable ; Interrupt Controller(PIC) 8259 (en écriture seule, si seulement ; le bit 4 de l'octet à écrire à cette adresse est à 0 et le bit 3 à 1). ; OCW3 equ 20h ; ; Adresse du registre Interrupt Request du PIC 8259 ; (en lecture seule, si seulement le bit 1 de OCW3 = 1 et le bit 0 ; de OCW3 = 0). ; IRR equ 20h ; ; Macro pour émuler une instruction POPF afin de régler le bogue de certains ; 80286 qui autorisent les interruptions pendant une POPF même si ; les interruptions restent désactivées. ; MPOPF macro local p1, p2 jmp short p2 p1: iret ;effectue un saut vers l'adresse ;empilée & pop les drapeaux p2: push cs ;construit l'adresse de retour far vers call p1 ;la prochaine instruction endm ; ; Macro pour retarder un peu afin qu'un laps de temps suffisant ; se fasse entre les accès E/S pour que le périphérique accédé ; puisse répondre même sur un PC rapide. ; DELAY macro jmp $+2 jmp $+2 jmp $+2 endm OriginalFlags db ? ; stockage de l'octet de poids fort ; du registre FLAGS avant ; l'appel à ZTimerOn TimedCount dw ? ; compteur du timer 0 quand ce dernier ; est arrêté ReferenceCount dw ? ; nombre de compte requis pour ; exécuter le surplus de code du timer OverflowFlag db ? ; utilisé pour indiquer le débordement ; du timer pendant la mesure ; ;Affichage d'une chaîne pour reporter les résultats. ; OutputStr label byte db 0dh,0ah, 'Temps mesuré: ', 5 dup (?) ASCIICountEnd label byte db ' microsecondes, 0dh, 0ah db '$' ; ; Affichage d'une chaîne pour reporter le débordement du timer. ; OverflowStr label byte db 0dh, 0ah db '****************************************************' db 0dh, 0ah db '* Le timer a débordé, l'intervalle à mesurer *' db 0dh, 0ah db '* était trop long pour le timer de précision. *' db 0dh, 0ah db '* Recommencez avec le *' db 0dh, 0ah db '* timer à période longue. *' db 0dh, 0ah db '****************************************************' db 0dh, 0ah db '$' ;******************************************************************** ;* Routine appelée pour commencer la mesure. * ;******************************************************************** ZTimerOn proc near ; ; Sauve le contexte du programme à mesurer. ; push ax pushf pop ax ; récupère les drapeaux pour garder ; les interruptions désactivées ; en sortie de routine mov cs:[OriginalFlags],ah ; mémorise l'état du ; drapeau Interruption and ah,0fdh ; force le drapeau Interruption empilé à 0 push ax ; ; Réactive les interruptions, l'interruption du timer peut ainsi se produire ; si elle est en attente. ; sti ; ; Force le timer 0 du 8253 en mode 2 (division par N), pour entraîner ; un comptage linéaire et non par deux. ; Laisse le 8253 attendre le chargement du compte initial du timer 0. ; mov al,00110100b ;mode 2 out MODE_8253,al ; ; Force le compteur du timer à 0, nous savons alors qu'il n'y aura ; pas d'autre interruption du timer. ; Note: ce qui introduit une inexactitude d'au moins 54 ms dans l'horloge ; système à chaque fois que la mesure est exécutée. ; DELAY sub al,al out TIMER_0_8253,al ;lsb DELAY out TIMER_0_8253,al ;msb ; ; Attendre avant de supprimer les interruptions pour prendre en compte ; l'interruption générée par le passage du mode 3 au mode 2. Le délai ; doit être au minimum de 210 ns pour que l'interruption ait le temps de ; se produire. 10 sauts sont ici utilisés pour laisser suffisamment de temps ; sur un PC très rapide. ; rept 10 jmp $+2 endm ; ; Désactive les interruptions pour obtenir une mesure précise. ; cli ; ; Force le compteur du timer à 0 de nouveau pour démarrer l'intervalle de mesure. ; mov al,00110100b ;prépare le chargement out MODE_8253,al ;du compte initial DELAY sub al,al out TIMER_0_8253,al ;charge lsb DELAY out TIMER_0_8253,al ;charge msb ; ; Restaure le contexte et retourne. ; MPOPF ;laisse les interruptions désactivées pop ax ret ZTimerOn endp ;******************************************************************** ;* Routine appelée pour arrêter la mesure et le récupérer sa valeur.* ;******************************************************************** ZTimerOff proc near ; ; Sauve le contexte du programme à mesurer. ; push ax push cx pushf ; ; verrouille le compteur. ; mov al,00000000b ;verrouille le timer 0 out MODE_8253,al ; ; Vérifie si le timer a débordé en demandant au 8259 si une interruption ; du timer est en attente. ; mov al,00001010b ;OCW3, prêt à lire out OCW3,al ;le registre Interrupt Request DELAY in al,IRR ;lit le registre Interrupt Request and al,1 ;AL est à 1 si IRQ0 (l'interruption ;du timer) est en attente mov cs:[OverflowFlag],al ;stocke l'état du débordement du timer ; ;Réactive les interruptions. ; sti ; ; Lit le compteur que nous avons verrouillé plus tôt. ; in al,TIMER_0_8253 ;octet de poids faible DELAY mov ah,al in al,TIMER_0_8253 ;octet de poids fort xchg ah,al neg ax ;convertit le décompte ;restant du temps écoulé mov cs:[TimedCount],ax ; Mesure d'un fragment de code de longueur nulle, pour référencer la taille du ; surplus de cette routine. 16 mesures sont faîtes et une moyenne plus ; précise arrondit le résultat. ; mov cs:[ReferenceCount],0 mov cx,16 cli ;les interruptions sont désactivées ; par souci de précision RefLoop: call ReferenceZTimerOn call ReferenceZTimerOff loop RefLoop sti add cs:[ReferenceCount],8 ;total + (0.5 * 16) mov cl,4 shr cs:[ReferenceCount],cl ;(total) / 16 + 0.5 ; ; Restaure l'état original des interruptions. ; pop ax ;récupère les drapeaux mov ch,cs:[OriginalFlags] ;récupère l'octet de poids fort ;d'origine du registre FLAGS and ch,not 0fdh ;ne s'intéresse qu'au ;drapeau d'interruption... and ah,0fdh ;et laisse les autres ;dans leur état courant or ah,ch ;rend aux mots des drapeaux ;leur drapeau d'interruption d'origine push ax ;prépare les drapeaux à empiler ; ; Restaure le contexte du programme à mesurer et y retourne. ; MPOPF ;restaure les drapeaux ; à leur état initial pop cx pop ax ret ZTimerOff endp ; ; Appelé par ZTimerOff pour démarrer le timer pour mesurer le surplus. ; ReferenceZTimerOn proc near ; ; Sauve le contexte du programme à mesurer. ; push ax pushf ;les interruptions sont déjà désactivées ; ; Force le timer 0 du 8253 en mode 2 (division par N), pour faire ; un décompte linéaire et non par deux. ; mov al,00110100b ;prépare le chargement out MODE_8253,al ;du compteur initial du timer DELAY ; ; Force le compteur initial du timer à 0. ; sub al,al out TIMER_0_8253,al ;charge lsb DELAY out TIMER_0_8253,al ;charge msb ; ; Restaure le contexte du programme à mesurer et y retourne. ; MPOPF pop ax ret ReferenceZTimerOn endp ; ; Appelé par ZTimerOff pour arrêter le timer et ajouter au résultat à ; ReferenceCount pour mesurer le surplus. ; ReferenceZTimerOff proc near ; ; Sauve le contexte du programme à mesurer. ; push ax push cx pushf ; ; Verrouille le décompte et le lit. ; mov al,00000000b ;Verrouille le timer 0 out MODE_8253,al DELAY in al,TIMER_0_8253;lsb DELAY mov ah,al in al,TIMER_0_8253;msb xchg ah,al neg ax ;convertit le ;reste à décompter add cs:[ReferenceCount],ax ; ; Restaure le contexte du programme à mesurer et y retourne. ; MPOPF pop cx pop ax ret ReferenceZTimerOff endp ;******************************************************************** ;* Routine appelée pour reporter les résultats. * ;******************************************************************** ZTimerReport proc near pushf push ax push bx push cx push dx push si push ds ; push cs ;les fonctions DOS demandent que DS pointe pop ds ;sur le texte à afficher assume ds:Code ; ; Vérifier si le timer 0 a débordé. ; cmp [OverflowFlag],0 jz PrintGoodCount mov dx,offset OverflowStr mov ah,9 int 21h jmp short EndZTimerReport ; ; Convertit le décompte en microsecondes décimales ASCII. ; PrintGoodCount: mov ax,[TimedCount] sub ax,[ReferenceCount] mov si,offset ASCIICountEnd - 1 ; ; Convertit le décompte en microsecondes en multipliant par 8381. ; mov dx,8381 mul dx mov bx,10000 div bx ;* .8381 = * 8381 / 10000 ; ; Convertit les microsecondes en 5 chiffres décimaux ASCII. ; mov bx,10 mov cx,5 CTSLoop: sub dx,dx div bx add dl,'0' mov [si],dl dec si loop CTSLoop ; ; Affiche les résultats. ; mov ah,9 mov dx,offset OutputStr int 21h ; EndZTimerReport: pop ds pop si pop dx pop cx pop bx pop ax MPOPF ret ZTimerReport endp Code ends end