home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / PROG / ASSEMBLY / TIMERZEN.ZIP / TIMER.ASM < prev   
Encoding:
Assembly Source File  |  1991-05-28  |  10.6 KB  |  441 lines

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