home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_02_01 / 2n01021a < prev    next >
Text File  |  1990-10-12  |  15KB  |  467 lines

  1. Listing 2.  Timer Package Source Code.
  2.  
  3.         TITLE   TIMER - STARLITE Timer System.
  4.  
  5. ;***    TIMER -- Lightweight Timer Package.
  6. ;
  7. ;1.     Functional Description.
  8. ;       This module of STARLITE contains all of the code
  9. ;       necessary to implement the timer object used inside
  10. ;       the system.
  11. ;
  12. ;       The system is implemented in a very straightforward
  13. ;       and fast way that also burns system memory pool.
  14. ;       It is an a priori assumption that these timers almost
  15. ;       never fire, they are usually set and reset very
  16. ;       frequently (thousands of times per second).
  17. ;
  18. ;       The basic implementation is that a block of memory
  19. ;       is allocated at initialization time, and that block
  20. ;       is used as an array of timer objects.  Exported handles
  21. ;       are offset addresses of the elements (relative to the
  22. ;       beginning of DOSDATA), since system pool is addressable
  23. ;       in DOSDATA.
  24. ;
  25. ;       Starts and stops are made very fast (in fact, single
  26. ;       instructions) while scans through the timer database
  27. ;       are actually fairly fast (at least, faster than a
  28. ;       linked list of them), although this is less important
  29. ;       since the timer database is scanned every 32ms
  30. ;       (basically, on a timer tick).
  31. ;
  32. ;       Allocations and deallocations also happen to be quick,
  33. ;       since the allocator just scans the array for a free
  34. ;       entry, and the deallocator just clears the "in use"
  35. ;       bit of an entry.
  36. ;
  37. ;2.     Modification History.
  38. ;       S. E. Jones     90/06/15.       Original for 1.0.
  39. ;
  40. ;3.     NOTICE.
  41. ;       This software is a derivative of the timer code
  42. ;       that is licensed as a part of General Software's
  43. ;       STARLITE architecture.  The code in this article
  44. ;       can be freely incorporated into programs without
  45. ;       licensing STARLITE architecture from General Software.
  46. ;
  47. ;4.     Build Environment.
  48. ;       MASM 5.10, no special switches.
  49.  
  50. ;       Here are some useful macros to define
  51. ;       procedures and call them.  They eliminate
  52. ;       the need to manually declare EXTRN's.
  53.  
  54. DefProc MACRO   name
  55.         PUBLIC  name
  56. name    PROC    NEAR
  57.         ENDM
  58.  
  59. EndProc MACRO   name
  60.         ret
  61.         ASSUME  NOTHING
  62. name    ENDP
  63.         ENDM
  64.  
  65. Pcall   MACRO   name
  66.         IF2
  67.         IFNDEF  name
  68.         EXTRN   name:NEAR
  69.         ENDIF
  70.         ENDIF
  71.         call    name
  72.         ENDM
  73.  
  74. longword        struc
  75. lo              dw      ?
  76. hi              dw      ?
  77. longword        ends
  78.  
  79. ;       Package constants.
  80.  
  81. MILLISECONDS_PER_TICK   = 55    ; time between ExpireTimer runs.
  82.  
  83. ;       Define a timer object.
  84.  
  85. TIMER           struc
  86. timer_timeout   dd      ?       ; absolute time of expiration.
  87. timer_rtn       dd      ?       ; address of routine to execute.
  88. timer_flags     dw      ?       ; conditions on this timer.
  89. timer_context   dw      ?       ; context value passed to routine.
  90. TIMER           ends
  91.  
  92. TIMER_FLAGS_ALLOCATED   = 0001h ; timer object is in use.
  93. TIMER_FLAGS_RUNNING     = 0002h ; timer is running.
  94.  
  95. TIMERCODE SEGMENT PARA PUBLIC 'CODE'
  96. TIMERCODE ENDS
  97.  
  98. CGROUP    GROUP TIMERCODE       ; add other code segs here.
  99.  
  100. TIMERDATA SEGMENT PARA PUBLIC 'DATA'
  101.  
  102. ;       The following statics are private to the package.
  103.  
  104. TimeCounter     dd      ?       ; milliseconds since power-on.
  105.  
  106. MAX_TIMERS      =       100     ; number of timers in system.
  107. StaticArray     db      MAX_TIMERS dup (type TIMER)
  108. TimerArray      dw      0       ; offset pointer to timer array.
  109.  
  110. TIMERDATA ENDS
  111.  
  112. DGROUP    GROUP TIMERDATA       ; add other data segs here.
  113.  
  114. TIMERCODE SEGMENT
  115.  
  116. ;***    InitializeTimerSystem - Initialize the Timer System.
  117. ;
  118. ;   FUNCTIONAL DESCRIPTION.
  119. ;       This routine is called by the application to
  120. ;       initialize the timer package.
  121. ;
  122. ;   ENTRY.
  123. ;       none.
  124. ;
  125. ;   EXIT.
  126. ;       none.
  127. ;
  128. ;   USES.
  129. ;       flags.
  130.  
  131.         ASSUME  CS:CGROUP, DS:NOTHING, ES:NOTHING, SS:NOTHING
  132. DefProc InitializeTimerSystem
  133.         push    ax
  134.         push    bx
  135.         push    cx
  136.         push    dx
  137.         push    si
  138.         push    di
  139.         push    ds
  140.         mov     ax, DGROUP
  141.         mov     ds, ax                  ; (DS) = DGROUP.
  142.         ASSUME  DS:DGROUP
  143.  
  144.         lea     di, CGROUP:StaticArray  ; (DI) = adr, database.
  145.         mov     TimerArray, di          ; save database address.
  146.  
  147. ;       Initialize timer database.
  148.  
  149.         mov     cx, MAX_TIMERS          ; (CX) = number of entries to reset.
  150. InitializeTimerSystem_Loop:
  151.         mov     timer_flags.[di], 0     ; reset this timer.
  152.         add     di, (SIZE TIMER)        ; (DI) = FWA, next timer.
  153.         loop    InitializeTimerSystem_Loop ; do the rest of 'em.
  154.  
  155. ;       Reset the current time up-counter.
  156.  
  157.         sub     ax, ax
  158.         mov     TimeCounter.lo, ax
  159.         mov     TimeCounter.hi, ax
  160.  
  161.         pop     ds
  162.         ASSUME  DS:NOTHING
  163.         pop     di
  164.         pop     si
  165.         pop     dx
  166.         pop     cx
  167.         pop     bx
  168.         pop     ax
  169. EndProc InitializeTimerSystem
  170.  
  171. ;***   ExpireTimers - Fire Any Expired Timers.
  172. ;
  173. ;   FUNCTIONAL DESCRIPTION.
  174. ;       This routine is called by interrupt 8 to expire
  175. ;       system timers.  Expired timers are called with
  176. ;       interrupts ENABLED and with a context in (BX).
  177. ;       All timer routines are called with FAR linkage,
  178. ;       so they must return with RETF.
  179. ;
  180. ;   ENTRY.
  181. ;       none.
  182. ;
  183. ;   EXIT.
  184. ;       none.
  185. ;
  186. ;   USES.
  187. ;       flags.
  188.  
  189.         ASSUME  CS:CGROUP, DS:NOTHING, ES:NOTHING, SS:NOTHING
  190. DefProc ExpireTimers, PUBLIC
  191.         push    ax
  192.         push    bx
  193.         push    cx
  194.         push    dx
  195.         push    si
  196.         push    di
  197.         push    ds
  198.         mov     ax, DGROUP
  199.         mov     ds, ax                  ; (DS) = DGROUP.
  200.         ASSUME  DS:DGROUP
  201.  
  202.         cli                             ; BEGIN CRITICAL SECTION.
  203.         add     TimeCounter.lo, MILLISECONDS_PER_TICK
  204.         adc     TimeCounter.hi, 0       ; do 32-bit update.
  205.         sti                             ; END CRITICAL SECTION.
  206.  
  207. ExpireTimers_Scan:
  208.         nop                             ; widen interrupt window.
  209.         nop
  210.         nop
  211.         mov     ax, DGROUP
  212.         mov     ds, ax                  ; (DS) = DGROUP.
  213.  
  214.         cli                             ; BEGIN CRITICAL SECTION.
  215.         mov     dx, TimeCounter.hi
  216.         mov     ax, TimeCounter.lo      ; (DX:AX) = current time.
  217.         mov     di, TimerArray          ; (DI) = FWA, 1st timer in system.
  218.         or      di, di                  ; is there a timer array?
  219.         jz      ExpireTimers_Exit       ; if not.
  220.         sub     di, (SIZE TIMER)        ; backup one for algorithm.
  221.         mov     cx, MAX_TIMERS          ; number of timers in system.
  222.  
  223. ExpireTimers_Loop:
  224.         dec     cx                      ; (CX) = one less timer to go.
  225.         jz      ExpireTimers_Exit       ; if there are no more, we're done.
  226.         add     di, (SIZE TIMER)        ; (DI) = FWA, next timer in system.
  227.  
  228.         test    timer_flags.[di], TIMER_FLAGS_ALLOCATED ; is the timer allocated?
  229.         jz      ExpireTimers_Loop       ; if not.
  230.         test    timer_flags.[di], TIMER_FLAGS_RUNNING ; is the timer running?
  231.         jz      ExpireTimers_Loop       ; if not.
  232.  
  233. ;       If the expiration time is greater than or equal to the current time,
  234. ;       then we call the timer routine and stop the timer.
  235.  
  236.         cmp     timer_timeout.hi.[di], dx ; is the timer's time > current time?
  237.         ja      ExpireTimers_Loop       ; if so.
  238.         jb      ExpireTimers_Expire     ; if expired.
  239.  
  240. ;       The most significant portions of the times match exactly.  Use the
  241. ;       low-order times to compare.
  242.  
  243.         cmp     timer_timeout.lo.[di], ax ; is the timer's time > current time?
  244.         ja      ExpireTimers_Loop       ; if so.
  245.  
  246. ;       The timer has expired.  Enable interrupts and call the routine.
  247.  
  248. ExpireTimers_Expire:
  249.         and     timer_flags.[di], NOT TIMER_FLAGS_RUNNING ; turn off running bit.
  250.         mov     timer_timeout.hi.[di], -1    ; extend timeout to make it fast.
  251.         mov     timer_timeout.lo.[di], -1
  252.         push    cs                      ; push return address on stack.
  253.         lea     ax, CGROUP:ExpireTimers_Scan
  254.         push    ax
  255.  
  256.         push    timer_rtn.hi.[di]       ; push target routine address on stack.
  257.         push    timer_rtn.lo.[di]
  258.  
  259.         mov     bx, timer_context.[di]  ; (BX) = context to pass to rtn.
  260.         sti                             ; END CRITICAL SECTION.
  261.         retf                            ; leap into timer context.
  262.  
  263. ;       We made it through the timer list without finding a timer.
  264.  
  265. ExpireTimers_Exit:
  266.         sti                             ; END CRITICAL SECTION.
  267.         pop     ds
  268.         ASSUME  DS:NOTHING
  269.         pop     di
  270.         pop     si
  271.         pop     dx
  272.         pop     cx
  273.         pop     bx
  274.         pop     ax
  275. EndProc ExpireTimers
  276.  
  277. ;***    AllocateTimer - Allocate a Timer Object.
  278. ;
  279. ;   FUNCTIONAL DESCRIPTION.
  280. ;       This routine is called to allocate a timer object
  281. ;       so that it may be used.  The object itself is
  282. ;       allocated internally to this module.  A handle to
  283. ;       the timer is returned to the caller.  In this actual
  284. ;       implementation, the handle is the offset address
  285. ;       from within DGROUP of the allocated timer object.
  286. ;
  287. ;       The caller also passes the address of a timer
  288. ;       expiration routine to execute and a context value.
  289. ;       These parameters are stored in the timer object.
  290. ;
  291. ;   ENTRY.
  292. ;       AX      - timer context.
  293. ;       BX:CX   - address, timer expiration routine.
  294. ;
  295. ;   EXIT.
  296. ;       CY      - clear if successful, else set if failure.
  297. ;       AX      - handle to timer if success.
  298. ;
  299. ;   USES.
  300. ;       AX, flags.
  301.  
  302.         ASSUME  CS:CGROUP, DS:NOTHING, ES:NOTHING, SS:NOTHING
  303. DefProc AllocateTimer
  304.         push    ds
  305.         push    di
  306.         push    dx
  307.         push    cx
  308.         mov     dx, cx                  ; (BX:DX) = FWA, expiration routine.
  309.  
  310.         mov     cx, DGROUP
  311.         mov     ds, cx                  ; (DS) = DGROUP.
  312.         ASSUME  DS:DGROUP
  313.  
  314. ;       Scan timer database looking for free timer object.
  315.  
  316.         mov     cx, MAX_TIMERS          ; (CX) = # of timers to scan.
  317.         mov     di, TimerArray          ; (DI) = FWA, 1st timer.
  318.         sub     di, (SIZE TIMER)        ; backup one for algorithm.
  319.         cli                             ; BEGIN CRITICAL SECTION.
  320. AllocateTimer_Loop:
  321.         add     di, (SIZE TIMER)        ; (DI) = FWA, next timer.
  322.         test    timer_flags.[di], TIMER_FLAGS_ALLOCATED ; is this timer in use?
  323.         jz      AllocateTimer_Found     ; nope.  we can use him.
  324.         loop    AllocateTimer_Loop      ; go scan the rest.
  325.  
  326. ;       We ran out of timers in the system.  Return with failure.
  327.  
  328.         sti                             ; END CRITICAL SETION.
  329.         pop     cx
  330.         pop     dx
  331.         pop     di
  332.         pop     ds
  333.         stc                             ; indicate failure.
  334.         ret                             ; return with failure.
  335.  
  336. ;       We found a vacant timer object.  Allocate it now.
  337.  
  338. AllocateTimer_Found:
  339.         mov     timer_flags.[di], TIMER_FLAGS_ALLOCATED ; reserve it.
  340.         mov     timer_context.[di], ax  ; save caller's context.
  341.         mov     timer_rtn.lo.[di], dx   ; save caller's expiration routine.
  342.         mov     timer_rtn.hi.[di], bx
  343.         mov     ax, di                  ; return timer handle.
  344.         sti                             ; END CRITICAL SETION.
  345.         pop     cx
  346.         pop     dx
  347.         pop     di
  348.         pop     ds
  349.         clc                             ; indicate success.
  350. EndProc AllocateTimer
  351.  
  352. ;***    DeallocateTimer - Deallocate a Timer Object.
  353. ;
  354. ;   FUNCTIONAL DESCRIPTION.
  355. ;       This routine is called to dispose of an allocated
  356. ;       timer object.
  357. ;
  358. ;   ENTRY.
  359. ;       AX      - handle to timer object.
  360. ;
  361. ;   EXIT.
  362. ;       none.
  363. ;
  364. ;   USES.
  365. ;       flags.
  366.  
  367.         ASSUME  CS:CGROUP, DS:NOTHING, ES:NOTHING, SS:NOTHING
  368. DefProc DeallocateTimer, PUBLIC
  369. ;       DISPLAY 'SysDeallocateTimer: Entered.'
  370.         push    ds
  371.         push    di
  372.         mov     di, DGROUP
  373.         mov     ds, di                  ; (DS) = DGROUP.
  374.         ASSUME  DS:DGROUP
  375.  
  376. ;       Simply clear the allocated bit, and we're done.
  377.  
  378.         mov     di, ax                  ; (DI) = FWA, timer object.
  379.         mov     timer_flags.[di], 0     ; clear allocated bit.
  380.  
  381.         pop     di
  382.         pop     ds
  383. EndProc DeallocateTimer
  384.  
  385. ;***    StartTimer - Schedule Timeout.
  386. ;
  387. ;   FUNCTIONAL DESCRIPTION.
  388. ;       This routine is called to start a timer, making
  389. ;       it eligible to receive a timeout interrupt.  All
  390. ;       we really do is set the RUNNING bitflag in the
  391. ;       timer object and calculate the timeout, which is
  392. ;       specified as a relative number of milliseconds and
  393. ;       is converted to an absolute millisecond value by
  394. ;       adding it to the current millisecond up-count.
  395. ;
  396. ;   ENTRY.
  397. ;       AX      - handle to timer object.
  398. ;       CX      - number of milliseconds until timer will expire.
  399. ;
  400. ;   EXIT.
  401. ;       none.
  402. ;
  403. ;   USES.
  404. ;       flags.
  405.  
  406.         ASSUME  CS:CGROUP, DS:NOTHING, ES:NOTHING, SS:NOTHING
  407. DefProc StartTimer, PUBLIC
  408.         push    ds
  409.         push    di
  410.         mov     di, ax                  ; (DI) = FWA, timer object.
  411.         mov     ax, DGROUP
  412.         mov     ds, ax                  ; (DS) = DGROUP.
  413.         ASSUME  DS:DGROUP
  414.  
  415. ;       Reset timeout value.  He specified relative
  416. ;       milliseconds in (AX), so we get absolute milli-
  417. ;       seconds (compatible with the units in TimeCounter)
  418. ;       by adding it to TimeCounter.
  419.  
  420.         cli                             ; BEGIN CRITICAL SECTION.
  421.         or      timer_flags.[di], TIMER_FLAGS_RUNNING
  422.         push    TimeCounter.lo          ; set timeout = current time.
  423.         push    TimeCounter.hi
  424.         pop     timer_timeout.hi.[di]
  425.         pop     timer_timeout.lo.[di]
  426.         add     timer_timeout.lo.[di], cx ; add the 16-bit delta time.
  427.         adc     timer_timeout.hi.[di], 0  ; get 32-bit arithmetic result.
  428.         sti                             ; END CRITICAL SECTION.
  429.  
  430.         pop     di
  431.         pop     ds
  432. EndProc StartTimer
  433.  
  434. ;***    StopTimer - Terminate Scheduled Timeout.
  435. ;
  436. ;   FUNCTIONAL DESCRIPTION.
  437. ;       This routine is called to stop a timer, making it
  438. ;       ineligible to receive a timeout interrupt.
  439. ;
  440. ;   ENTRY.
  441. ;       AX      - handle to timer object.
  442. ;
  443. ;   EXIT.
  444. ;       none.
  445. ;
  446. ;   USES.
  447. ;       flags.
  448.  
  449.         ASSUME  CS:CGROUP, DS:NOTHING, ES:NOTHING, SS:NOTHING
  450. DefProc StopTimer, PUBLIC
  451.         push    ds
  452.         push    di
  453.         mov     di, ax                  ; (DI) = FWA, timer object.
  454.         mov     ax, DGROUP
  455.         mov     ds, ax                  ; (DS) = DGROUP.
  456.         ASSUME  DS:DGROUP
  457.  
  458.         and     timer_flags.[di], NOT TIMER_FLAGS_RUNNING
  459.  
  460.         pop     di
  461.         pop     ds
  462. EndProc StopTimer
  463.  
  464. TIMERCODE ENDS
  465.  
  466.         END
  467.