home *** CD-ROM | disk | FTP | other *** search
/ Graphics Programming Black Book (Special Edition) / BlackBook.bin / disk1 / source / chapterk / pcztfar.asm < prev    next >
Encoding:
Assembly Source File  |  1997-06-18  |  14.0 KB  |  442 lines

  1. ; ****PCZTFAR.ASM
  2. ; The C-far-callable version of the precision Zen timer
  3. ;       (PZTIMER.ASM)
  4. ;
  5. ; Uses the 8253 timer to time the performance of code that takes
  6. ; less than about 54 milliseconds to execute, with a resolution
  7. ; of better than 10 microseconds.
  8. ;
  9. ; By Michael Abrash 4/26/89
  10. ;
  11. ; Externally callable routines:
  12. ;
  13. ;  ZTimerOn: Starts the Zen timer, with interrupts disabled.
  14. ;
  15. ;  ZTimerOff: Stops the Zen timer, saves the timer count,
  16. ;       times the overhead code, and restores interrupts to the
  17. ;       state they were in when ZTimerOn was called.
  18. ;
  19. ;  ZTimerReport: Prints the net time that passed between starting
  20. ;       and stopping the timer.
  21. ;
  22. ; Note: If longer than about 54 ms passes between ZTimerOn and
  23. ;       ZTimerOff calls, the timer turns over and the count is
  24. ;       inaccurate. When this happens, an error message is displayed
  25. ;       instead of a count. The long-period Zen timer should be used
  26. ;       in such cases.
  27. ;
  28. ; Note: Interrupts *MUST* be left off between calls to ZTimerOn
  29. ;       and ZTimerOff for accurate timing and for detection of
  30. ;       timer overflow.
  31. ;
  32. ; Note: These routines can introduce slight inaccuracies into the
  33. ;       system clock count for each code section timed even if
  34. ;       timer 0 doesn't overflow. If timer 0 does overflow, the
  35. ;       system clock can become slow by virtually any amount of
  36. ;       time, since the system clock can't advance while the
  37. ;       precison timer is timing. Consequently, it's a good idea
  38. ;       to reboot at the end of each timing session. (The
  39. ;       battery-backed clock, if any, is not affected by the Zen
  40. ;       timer.)
  41. ;
  42. ; All registers, and all flags except the interrupt flag, are
  43. ; preserved by all routines. Interrupts are enabled and then disabled
  44. ; by ZTimerOn, and are restored by ZTimerOff to the state they were
  45. ; in when ZTimerOn was called.
  46. ;
  47.  
  48. PZTIMER_TEXT    segment word public 'CODE'
  49.         assume  cs:PZTIMER_TEXT, ds:nothing
  50.         public  _ZTimerOn, _ZTimerOff, _ZTimerReport
  51.  
  52. ;
  53. ; Base address of the 8253 timer chip.
  54. ;
  55. BASE_8253               equ     40h
  56. ;
  57. ; The address of the timer 0 count registers in the 8253.
  58. ;
  59. TIMER_0_8253            equ     BASE_8253 + 0
  60. ;
  61. ; The address of the mode register in the 8253.
  62. ;
  63. MODE_8253               equ     BASE_8253 + 3
  64. ;
  65. ; The address of Operation Command Word 3 in the 8259 Programmable
  66. ; Interrupt Controller (PIC) (write only, and writable only when
  67. ; bit 4 of the byte written to this address is 0 and bit 3 is 1).
  68. ;
  69. OCW3                    equ     20h
  70. ;
  71. ; The address of the Interrupt Request register in the 8259 PIC
  72. ; (read only, and readable only when bit 1 of OCW3 = 1 and bit 0
  73. ; of OCW3 = 0).
  74. ;
  75. IRR                     equ     20h
  76. ;
  77. ; Macro to emulate a POPF instruction in order to fix the bug in some
  78. ; 80286 chips which allows interrupts to occur during a POPF even when
  79. ; interrupts remain disabled.
  80. ;
  81. MPOPF macro
  82.         local   p1, p2
  83.         jmp short p2
  84. p1:     iret                    ;jump to pushed address & pop flags
  85. p2:     push    cs              ;construct far return address to
  86.         call    p1              ; the next instruction
  87.         endm
  88.  
  89. ;
  90. ; Macro to delay briefly to ensure that enough time has elapsed
  91. ; between successive I/O accesses so that the device being accessed
  92. ; can respond to both accesses even on a very fast PC.
  93. ;
  94. DELAY   macro
  95.         jmp     $+2
  96.         jmp     $+2
  97.         jmp     $+2
  98.         endm
  99.  
  100. OriginalFlags           db      ?       ;storage for upper byte of
  101.                                         ; FLAGS register when
  102.                                         ; ZTimerOn called
  103. TimedCount              dw      ?       ;timer 0 count when the timer
  104.                                         ; is stopped
  105. ReferenceCount          dw      ?       ;number of counts required to
  106.                                         ; execute timer overhead code
  107. OverflowFlag            db      ?       ;used to indicate whether the
  108.                                         ; timer overflowed during the
  109.                                         ; timing interval
  110. ;
  111. ; String printed to report results.
  112. ;
  113. OutputStr       label   byte
  114.                 db      'Timed count: ', 5 dup (?)
  115. ASCIICountEnd   label   byte
  116.                 db      ' microseconds', 0dh, 0ah
  117.                 db      '$'
  118. ;
  119. ; String printed to report timer overflow.
  120. ;
  121. OverflowStr     label   byte
  122.         db      0dh, 0ah
  123.         db      '****************************************************'
  124.         db      0dh, 0ah
  125.         db      '* The timer overflowed, so the interval timed was  *'
  126.         db      0dh, 0ah
  127.         db      '* too long for the precision timer to measure.     *'
  128.         db      0dh, 0ah
  129.         db      '* Please perform the timing test again with the    *'
  130.         db      0dh, 0ah
  131.         db      '* long-period timer.                               *'
  132.         db      0dh, 0ah
  133.         db      '****************************************************'
  134.         db      0dh, 0ah
  135.         db      '$'
  136.  
  137. ;********************************************************************
  138. ;* Routine called to start timing.                                  *
  139. ;********************************************************************
  140.  
  141. _ZTimerOn       proc    far
  142.  
  143. ;
  144. ; Save the context of the program being timed.
  145. ;
  146.         push    ax
  147.         pushf
  148.         pop     ax                      ;get flags so we can keep
  149.                                         ; interrupts off when leaving
  150.                                         ; this routine
  151.         mov     cs:[OriginalFlags],ah   ;remember the state of the
  152.                                         ; Interrupt flag
  153.         and     ah,0fdh                 ;set pushed interrupt flag
  154.                                         ; to 0
  155.         push    ax
  156. ;
  157. ; Turn on interrupts, so the timer interrupt can occur if it's
  158. ; pending.
  159. ;
  160.         sti
  161. ;
  162. ; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
  163. ; linear counting rather than count-by-two counting. Also
  164. ; leaves the 8253 waiting for the initial timer 0 count to
  165. ; be loaded.
  166. ;
  167.         mov     al,00110100b            ;mode 2
  168.         out     MODE_8253,al
  169. ;
  170. ; Set the timer count to 0, so we know we won't get another
  171. ; timer interrupt right away.
  172. ; Note: this introduces an inaccuracy of up to 54 ms in the system
  173. ; clock count each time it is executed.
  174. ;
  175.         DELAY
  176.         sub     al,al
  177.         out     TIMER_0_8253,al         ;lsb
  178.         DELAY
  179.         out     TIMER_0_8253,al         ;msb
  180. ;
  181. ; Wait before clearing interrupts to allow the interrupt generated
  182. ; when switching from mode 3 to mode 2 to be recognized. The delay
  183. ; must be at least 210 ns long to allow time for that interrupt to
  184. ; occur. Here, 10 jumps are used for the delay to ensure that the
  185. ; delay time will be more than long enough even on a very fast PC.
  186. ;
  187.         rept 10
  188.         jmp     $+2
  189.         endm
  190. ;
  191. ; Disable interrupts to get an accurate count.
  192. ;
  193.         cli
  194. ;
  195. ; Set the timer count to 0 again to start the timing interval.
  196. ;
  197.         mov     al,00110100b            ;set up to load initial
  198.         out     MODE_8253,al            ; timer count
  199.         DELAY
  200.         sub     al,al
  201.         out     TIMER_0_8253,al         ;load count lsb
  202.         DELAY
  203.         out     TIMER_0_8253,al         ;load count msb
  204. ;
  205. ; Restore the context and return.
  206. ;
  207.         MPOPF                           ;keeps interrupts off
  208.         pop     ax
  209.         ret
  210.  
  211. _ZTimerOn       endp
  212.  
  213. ;********************************************************************
  214. ;* Routine called to stop timing and get count.                     *
  215. ;********************************************************************
  216.  
  217. _ZTimerOff proc far
  218.  
  219. ;
  220. ; Save the context of the program being timed.
  221. ;
  222.         push    ax
  223.         push    cx
  224.         pushf
  225. ;
  226. ; Latch the count.
  227. ;
  228.         mov     al,00000000b            ;latch timer 0
  229.         out     MODE_8253,al
  230. ;
  231. ; See if the timer has overflowed by checking the 8259 for a pending
  232. ; timer interrupt.
  233. ;
  234.         mov     al,00001010b            ;OCW3, set up to read
  235.         out     OCW3,al                 ; Interrupt Request register
  236.         DELAY
  237.         in      al,IRR                  ;read Interrupt Request
  238.                                         ; register
  239.         and     al,1                    ;set AL to 1 if IRQ0 (the
  240.                                         ; timer interrupt) is pending
  241.         mov     cs:[OverflowFlag],al    ;store the timer overflow
  242.                                         ; status
  243. ;
  244. ; Allow interrupts to happen again.
  245. ;
  246.         sti
  247. ;
  248. ; Read out the count we latched earlier.
  249. ;
  250.         in      al,TIMER_0_8253         ;least significant byte
  251.         DELAY
  252.         mov     ah,al
  253.         in      al,TIMER_0_8253         ;most significant byte
  254.         xchg    ah,al
  255.         neg     ax                      ;convert from countdown
  256.                                         ; remaining to elapsed
  257.                                         ; count
  258.         mov     cs:[TimedCount],ax
  259. ; Time a zero-length code fragment, to get a reference for how
  260. ; much overhead this routine has. Time it 16 times and average it,
  261. ; for accuracy, rounding the result.
  262. ;
  263.         mov     cs:[ReferenceCount],0
  264.         mov     cx,16
  265.         cli                             ;interrupts off to allow a
  266.                                         ; precise reference count
  267. RefLoop:
  268.         call    far ptr ReferenceZTimerOn
  269.         call    far ptr ReferenceZTimerOff
  270.         loop    RefLoop
  271.         sti
  272.         add     cs:[ReferenceCount],8   ;total + (0.5 * 16)
  273.         mov     cl,4
  274.         shr     cs:[ReferenceCount],cl  ;(total) / 16 + 0.5
  275. ;
  276. ; Restore original interrupt state.
  277. ;
  278.         pop     ax                      ;retrieve flags when called
  279.         mov     ch,cs:[OriginalFlags]   ;get back the original upper
  280.                                         ; byte of the FLAGS register
  281.         and     ch,not 0fdh             ;only care about original
  282.                                         ; interrupt flag...
  283.         and     ah,0fdh                 ;...keep all other flags in
  284.                                         ; their current condition
  285.         or      ah,ch                   ;make flags word with original
  286.                                         ; interrupt flag
  287.         push    ax                      ;prepare flags to be popped
  288. ;
  289. ; Restore the context of the program being timed and return to it.
  290. ;
  291.         MPOPF                           ;restore the flags with the
  292.                                         ; original interrupt state
  293.         pop     cx
  294.         pop     ax
  295.         ret
  296.  
  297. _ZTimerOff endp
  298.  
  299. ;
  300. ; Called by ZTimerOff to start timer for overhead measurements.
  301. ;
  302.  
  303. ReferenceZTimerOn       proc    far
  304. ;
  305. ; Save the context of the program being timed.
  306. ;
  307.         push    ax
  308.         pushf           ;interrupts are already off
  309. ;
  310. ; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
  311. ; linear counting rather than count-by-two counting.
  312. ;
  313.         mov     al,00110100b    ;set up to load
  314.         out     MODE_8253,al    ; initial timer count
  315.         DELAY
  316. ;
  317. ; Set the timer count to 0.
  318. ;
  319.         sub     al,al
  320.         out     TIMER_0_8253,al ;load count lsb
  321.         DELAY
  322.         out     TIMER_0_8253,al ;load count msb
  323. ;
  324. ; Restore the context of the program being timed and return to it.
  325. ;
  326.         MPOPF
  327.         pop     ax
  328.         ret
  329.  
  330. ReferenceZTimerOn       endp
  331.  
  332. ;
  333. ; Called by ZTimerOff to stop timer and add result to ReferenceCount
  334. ; for overhead measurements.
  335. ;
  336.  
  337. ReferenceZTimerOff proc far
  338. ;
  339. ; Save the context of the program being timed.
  340. ;
  341.         push    ax
  342.         push    cx
  343.         pushf
  344. ;
  345. ; Latch the count and read it.
  346. ;
  347.         mov     al,00000000b            ;latch timer 0
  348.         out     MODE_8253,al
  349.         DELAY
  350.         in      al,TIMER_0_8253         ;lsb
  351.         DELAY
  352.         mov     ah,al
  353.         in      al,TIMER_0_8253         ;msb
  354.         xchg    ah,al
  355.         neg     ax                      ;convert from countdown
  356.                                         ; remaining to amount
  357.                                         ; counted down
  358.         add     cs:[ReferenceCount],ax
  359. ;
  360. ; Restore the context of the program being timed and return to it.
  361. ;
  362.         MPOPF
  363.         pop     cx
  364.         pop     ax
  365.         ret
  366.  
  367. ReferenceZTimerOff endp
  368.  
  369. ;********************************************************************
  370. ;* Routine called to report timing results.                         *
  371. ;********************************************************************
  372.  
  373. _ZTimerReport   proc    far
  374.  
  375.         pushf
  376.         push    ax
  377.         push    bx
  378.         push    cx
  379.         push    dx
  380.         push    si
  381.         push    ds
  382. ;
  383.         push    cs      ;DOS functions require that DS point
  384.         pop     ds      ; to text to be displayed on the screen
  385.         assume  ds:PZTIMER_TEXT
  386. ;
  387. ; Check for timer 0 overflow.
  388. ;
  389.         cmp     [OverflowFlag],0
  390.         jz      PrintGoodCount
  391.         mov     dx,offset OverflowStr
  392.         mov     ah,9
  393.         int     21h
  394.         jmp     short EndZTimerReport
  395. ;
  396. ; Convert net count to decimal ASCII in microseconds.
  397. ;
  398. PrintGoodCount:
  399.         mov     ax,[TimedCount]
  400.         sub     ax,[ReferenceCount]
  401.         mov     si,offset ASCIICountEnd - 1
  402. ;
  403. ; Convert count to microseconds by multiplying by .8381.
  404. ;
  405.         mov     dx,8381
  406.         mul     dx
  407.         mov     bx,10000
  408.         div     bx              ;* .8381 = * 8381 / 10000
  409. ;
  410. ; Convert time in microseconds to 5 decimal ASCII digits.
  411. ;
  412.         mov     bx,10
  413.         mov     cx,5
  414. CTSLoop:
  415.         sub     dx,dx
  416.         div     bx
  417.         add     dl,'0'
  418.         mov     [si],dl
  419.         dec     si
  420.         loop    CTSLoop
  421. ;
  422. ; Print the results.
  423. ;
  424.         mov     ah,9
  425.         mov     dx,offset OutputStr
  426.         int     21h
  427. ;
  428. EndZTimerReport:
  429.         pop     ds
  430.         pop     si
  431.         pop     dx
  432.         pop     cx
  433.         pop     bx
  434.         pop     ax
  435.         MPOPF
  436.         ret
  437.  
  438. _ZTimerReport   endp
  439.  
  440. PZTIMER_TEXT    ends
  441.         end
  442.