home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 19 / CD_ASCQ_19_010295.iso / dos / prg / midas / timer.asm < prev    next >
Assembly Source File  |  1994-08-06  |  24KB  |  968 lines

  1. ;*      TIMER.ASM
  2. ;*
  3. ;* TempoTimer, v1.10
  4. ;*
  5. ;* Copyright 1994 Petteri Kangaslampi and Jarno Paananen
  6. ;*
  7. ;* This file is part of the MIDAS Sound System, and may only be
  8. ;* used, modified and distributed under the terms of the MIDAS
  9. ;* Sound System license, LICENSE.TXT. By continuing to use,
  10. ;* modify or distribute this file you indicate that you have
  11. ;* read the license and understand and accept it fully.
  12. ;*
  13.  
  14.  
  15. IDEAL
  16. P386
  17. JUMPS
  18.  
  19. INCLUDE "lang.inc"
  20. INCLUDE "mglobals.inc"
  21. INCLUDE "errors.inc"
  22. INCLUDE "timer.inc"
  23. INCLUDE "ems.inc"
  24. INCLUDE "dma.inc"
  25. INCLUDE "dsm.inc"
  26. INCLUDE "sdevice.inc"
  27.  
  28.  
  29. NTPRATE = 100                           ; non-tempoPolling SoundDevice
  30.                     ; interrupt rate if not synchronized
  31.                     ; to screen (in Hz)
  32.  
  33. FRAMETIME = 965         ; Time between two interrupts is 96.5%
  34.                 ; of total frame time - the interrupt comes
  35.                 ; somewhat _before_ the Vertical Retrace
  36.                 ; actually starts.
  37.  
  38.  
  39. ENUM    tmrStates \                     ; timer state
  40.         tmrSystem, \                    ; system timer
  41.         tmrPlayer, \                    ; music player timer
  42.         tmrScreen                       ; Vertical Retrace timer
  43.  
  44.  
  45.  
  46. ;/***************************************************************************\
  47. ;*
  48. ;* Macro:       SetBorder color
  49. ;*
  50. ;* Description: Sets the border color if NOBORDERCOLOR is not defined
  51. ;*
  52. ;* Input:       color           border color
  53. ;*
  54. ;* Destroys:    none
  55. ;*
  56. ;\***************************************************************************/
  57.  
  58. MACRO   SetBorder       color
  59. IFNDEF NOBORDERCOLOR
  60.     push    dx ax
  61.     mov    dx,03DAh
  62.     in    al,dx
  63.     mov    dx,03C0h
  64.     mov    al,31h
  65.     out    dx,al
  66.     mov    al,color
  67.     out    dx,al
  68.     pop    ax dx
  69. ENDIF
  70. ENDM
  71.  
  72.  
  73.  
  74.  
  75. ;/***************************************************************************\
  76. ;*
  77. ;* Macro:       WaitNextVR
  78. ;*
  79. ;* Description: Waits for next Vertical Retrace
  80. ;*
  81. ;\***************************************************************************/
  82.  
  83. MACRO   WaitNextVR
  84. LOCAL    w1, w2
  85.  
  86.     mov    dx,03DAh
  87. w1:    in    al,dx        ; wait for a non-retrace period
  88.     test    al,8
  89.     jnz    w1
  90.  
  91. w2:    in    al,dx
  92.     test    al,8        ; wait for retrace
  93.     jz    w2
  94. ENDM
  95.  
  96.  
  97.  
  98.  
  99. DATASEG
  100.  
  101.  
  102. systemTimer    DD    ?        ; pointer to system timer routine
  103. sysTmrCount    DD    ?        ; system timer counter
  104.  
  105. playCount    DD    ?        ; player timer counter
  106. playTmrCount    DD    ?        ; initial value for player timer count
  107. sdev        DD    ?        ; pointer to Sound Device
  108. modPlay     DD    ?        ; pointer to module playing routine
  109. playMusic    DW    ?        ; 1 if music is to be played
  110. plTimer     DW    ?        ; 1 if player-timer is active
  111. plError         DW      ?               ; music playing error code
  112. plCallMP        DW      ?               ; call music player?
  113.  
  114. scrCount    DD    ?        ; Retrace timer counter
  115. scrTmrCount    DD    ?        ; initial value for VR timer counter
  116. scrPVCount    DD    ?        ; timer count for time before Retrace
  117. preVR        DD    ?        ; pre-VR function
  118. immVR           DD      ?               ; immVR()
  119. inVR            DD      ?               ; inVR()
  120. scrSync     DW    ?        ; 1 if timer is synchronized to screen
  121. scrTimer    DW    ?        ; 1 if screen-timer is active
  122. scrPlayer    DW    ?        ; synchronize player to screen?
  123.  
  124. tmrState    DW    ?        ; timer state
  125.  
  126. sysTimer    DW    ?        ; system timer active?
  127.  
  128.  
  129.  
  130.  
  131. CODESEG
  132.  
  133.  
  134.  
  135.  
  136. ;/***************************************************************************\
  137. ;*
  138. ;* Function:    setCount
  139. ;*
  140. ;* Description: Set timer count and restart timer
  141. ;*
  142. ;* Input:    bx        timer count
  143. ;*
  144. ;* Destroys:    al
  145. ;*
  146. ;\***************************************************************************/
  147.  
  148. PROC NOLANGUAGE setCount     NEAR    ; set timer counter and restart
  149.  
  150.     mov    al,30h            ; counter mode 0 - interrupt on
  151.     out    43h,al            ; terminal count
  152.     mov    al,bl
  153.     out    40h,al            ; set timer count and restart timer
  154.     mov    al,bh
  155.     out    40h,al
  156.  
  157.     ret
  158. ENDP
  159.  
  160.  
  161.  
  162.  
  163. ;/***************************************************************************\
  164. ;*
  165. ;* Function:    nextTimer
  166. ;*
  167. ;* Description: Prepare for next timer interrupt
  168. ;*
  169. ;* Destroys:    eax, ebx
  170. ;*
  171. ;\***************************************************************************/
  172.  
  173. PROC NOLANGUAGE nextTimer    NEAR
  174.  
  175.     cmp    [scrSync],1        ; is timer synchronized to screen?
  176.     jne    @@noscr
  177.  
  178.     cmp    [playMusic],1        ; should music be played?
  179.     jne    @@scr
  180.  
  181.     mov    ebx,[playCount]     ; player timer count
  182.     or    ebx,ebx         ; negative
  183.     jns    @@nos1
  184.     mov    ebx,10            ; make sure count is not negative
  185.     mov    [playCount],10
  186.     jmp    @@setpl
  187. @@nos1:
  188.     cmp    ebx,[scrCount]        ; will player timer come before scr?
  189.     jl    @@setpl
  190.  
  191. @@scr:    mov    ebx,[scrCount]        ; screen timer count
  192.     or    ebx,ebx         ; negative?
  193.     jns    @@nos2
  194.     mov    ebx,10            ; make sure count is not negative
  195.     mov    [scrCount],10
  196. @@nos2:
  197.     mov    [tmrState],tmrScreen    ; next interrupt will be screen timer
  198.     call    setCount        ; set count and restart timer
  199.     jmp    @@done
  200.  
  201.  
  202. @@setpl:
  203.     mov    [tmrState],tmrPlayer    ; next interrupt will be player
  204.     call    setCount        ; set count and restart
  205.     jmp    @@done
  206.  
  207.  
  208. @@noscr:
  209.     cmp    [playMusic],1        ; should music be played?
  210.     jne    @@sys
  211.  
  212.     mov    [tmrState],tmrPlayer
  213.     mov    ebx,[playCount]     ; player timer count
  214.     or    ebx,ebx         ; negative?
  215.     jns    @@1
  216.  
  217.     mov    ebx,10            ; make sure count is not negative
  218.     mov    [playCount],10
  219.  
  220. @@1:    call    setCount
  221.     jmp    @@done
  222.  
  223.  
  224. @@sys:                    ; system timer only
  225.     mov    [tmrState],tmrSystem    ; next int is system timer
  226.     xor    bx,bx
  227.     call    setCount        ; set system timer count
  228.  
  229. @@done:
  230.     ret
  231. ENDP
  232.  
  233.  
  234.  
  235.  
  236.  
  237.  
  238. ;/***************************************************************************\
  239. ;*
  240. ;* Function:    timer
  241. ;*
  242. ;* Description: timer interrupt handler
  243. ;*
  244. ;\***************************************************************************/
  245.  
  246. PROC NOLANGUAGE timer            ; timer interrupt
  247.  
  248.     pushad
  249.     push    ds es fs gs
  250.  
  251.         SetBorder 15
  252.  
  253.     mov    ax,@data
  254.     mov    ds,ax            ; set valid values to segment
  255.         mov     es,ax                   ; registers
  256.     mov    gs,ax
  257.  
  258.     cmp    [tmrState],tmrScreen    ; screen timer interrupt?
  259.     je    @@scrtmr
  260.     cmp    [tmrState],tmrSystem    ; system timer only?
  261.     je    @@systmr
  262.     cmp    [tmrState],tmrPlayer    ; player timer?
  263.     je    @@plrtmr
  264.     jmp    @@systmr        ; do _something_
  265.  
  266.  
  267. @@scrtmr:
  268.     cli                ; no interrupts here!
  269.  
  270.     SetBorder 14
  271.  
  272.     cmp    [scrTimer],1        ; is screen timer already active?
  273.     jne    @@scrnot
  274.  
  275.     ; screen timer already active - PANIC!
  276.  
  277.     mov    eax,[scrCount]
  278.     add    eax,[scrPVCount]
  279.     sub    [playCount],eax     ; update player timer counter
  280.     add    [sysTmrCount],eax    ; update system timer counter
  281.  
  282.     mov    eax,[scrTmrCount]    ; reset screen timer counter
  283.     mov    [scrCount],eax
  284.  
  285.     call    nextTimer        ; next timer interrupt
  286.  
  287.     mov    al,20h            ; send End Of Interrupt
  288.     out    20h,al
  289.     sti                ; enable interrupts
  290.     jmp    @@done            ; stop processing this interrupt
  291.  
  292.  
  293. @@scrnot:
  294.     cmp    [scrSync],1        ; should timer be synchronized to
  295.     jne    @@chksys        ; screen?
  296.  
  297.     mov    [scrTimer],1        ; screen-timer is now active
  298.  
  299.     mov    dx,03DAh
  300. @@wnvr: in    al,dx            ; wait until we are _not_ in a
  301.     test    al,8            ; retrace (just to make sure...)
  302.     jnz    @@wnvr
  303.  
  304.     cmp    [preVR],0
  305.         je      @@npvr                  ; call preVR() if pointer is not
  306.         call    [dword preVR]           ; NULL
  307. @@npvr:
  308.  
  309.     SetBorder 1
  310.     mov    eax,[scrCount]
  311.     add    eax,[scrPVCount]    ; update timer counters
  312.     add    [sysTmrCount],eax
  313.  
  314.     cmp    [scrPlayer],1        ; synchronize player to screen?
  315.     je    @@syncpl
  316.     sub    [playCount],eax     ; no, update count
  317.     jmp    @@scd
  318.  
  319. @@syncpl:
  320.     mov    eax,[playTmrCount]    ; synchronize player
  321.     mov    [playCount],eax
  322.  
  323. @@scd:
  324.     mov    eax,[scrTmrCount]    ; reset screen-interrupt count
  325.     mov    [scrCount],eax
  326.  
  327.     mov    dx,03DAh
  328. @@wvr:    in    al,dx            ; wait for the retrace
  329.     test    al,8
  330.     jz    @@wvr
  331.  
  332.         cmp     [immVR],0
  333.         je      @@nivr                  ; call immVR() if pointer is not
  334.         call    [dword immVR]           ; NULL
  335.  
  336. @@nivr:
  337.     SetBorder 2
  338.  
  339.     call    nextTimer        ; next timer iterrupt
  340.  
  341.     mov    [scrTimer],0        ; screen-timer (almost) finished
  342.  
  343.     SetBorder 4
  344.  
  345.     sti                ; enable interrupts now
  346.  
  347.     mov    al,20h            ; send End Of Interrupt to Interrupt
  348.     out    20h,al            ; Controller
  349.  
  350.  
  351.         cmp     [inVR],0
  352.         je      @@nvr                   ; call inVR() if pointer is not NULL
  353.         call    [dword inVR]
  354.  
  355. @@nvr:
  356.     SetBorder 0
  357.     mov    [scrTimer],0
  358.     jmp    @@chksys        ; check if system timer should be
  359.                     ; called
  360.  
  361.  
  362.  
  363.  
  364.  
  365. @@plrtmr:
  366.         ;SetBorder 7
  367.     cmp    [plTimer],1        ; is player timer already active?
  368.     jne    @@plnot         ; if not, it's OK to continue
  369.  
  370.     ; player timer already active - PANIC!
  371.  
  372.     mov    eax,[playCount]     ; previous player timer count
  373.     sub    [scrCount],eax        ; update screen timer count
  374.     add    [sysTmrCount],eax    ; and system timer count
  375.     mov    eax,[playTmrCount]    ; reset player timer count
  376.     mov    [playCount],eax
  377.     call    nextTimer        ; next timer interrupt (hopefully
  378.                     ; not player anymore...)
  379.     sti                ; enable interrupts
  380.     mov    al,20h            ; send End Of Interrupt
  381.     out    20h,al
  382.     jmp    @@done            ; quit interrupt - no playing until
  383.                     ; the previous player interrupt has
  384.                     ; finished
  385.  
  386. @@plnot:
  387.         mov     [plTimer],1             ; player timer is active
  388.     mov    eax,[playCount]     ; player timer count
  389.     or    eax,eax
  390.     js    @@pn1
  391.     sub    [scrCount],eax        ; update screen timer count
  392.     add    [sysTmrCount],eax    ; increase system timer count
  393. @@pn1:
  394.     cmp    [scrPlayer],1        ; synchronize player to screen?
  395.     je    @@pspl
  396.     mov    ebx,[playTmrCount]    ; new player timer count
  397.     mov    [playCount],ebx     ; set player timer count to counter
  398.     jmp    @@pnt
  399. @@pspl:
  400.     mov    [playCount],0FFFFh    ; make sure that next interrupt will
  401. @@pnt:                    ; be screen, not timer
  402.  
  403.     call    nextTimer        ; next timer interrupt
  404.  
  405.     sti                ; enable interrupts
  406.     mov    al,20h            ; send End Of Interrupt to Interrupt
  407.     out    20h,al            ; Controller
  408.  
  409.     cmp    [playMusic],1        ; should music be played?
  410.     je    @@playmus
  411.  
  412.     mov    [plTimer],0        ; player timer not active
  413.     jmp    @@chksys        ; call system timer if appropriate
  414.  
  415. @@playmus:
  416.         cmp     [plError],0             ; error during playing?
  417.         jne     @@pl1
  418.  
  419.     mov    edi,[playTmrCount]    ; store player timer count
  420.  
  421.     les    si,[sdev]            ; if using a non-tempoPoll
  422.     cmp    [es:si+SoundDevice.tempoPoll],1 ; Sound Device, get DMA play
  423.     je    @@nodma             ; position and store it for
  424.         call    dmaGetPos LANG, seg dsmBuffer offset dsmBuffer, \
  425.                 seg dsmDMAPos offset dsmDMAPos
  426.         test    ax,ax
  427.         jnz     @@plerr
  428.  
  429. @@nodma:
  430.     SetBorder 14
  431.  
  432.     cmp    [useEMS],1        ; is EMS used?
  433.     jne    @@play
  434.     call    emsSave LANG        ; save EMS mappings
  435.         test    ax,ax
  436.         jnz     @@plerr
  437.  
  438.         call    emsSafe LANG            ; set EMS "safe"-flag on
  439.         test    ax,ax
  440.         jnz     @@plerr
  441.  
  442. @@play:
  443.     SetBorder 15
  444.         ; Update Sound Device register / mix data:
  445.     les    si,[sdev]        ; point es:bx to Sound Device
  446.         call    [es:si+SoundDevice.Play] LANG, seg plCallMP offset plCallMP
  447.     SetBorder 2
  448.         test    ax,ax
  449.         jnz     @@plerr
  450.  
  451.         cmp     [plCallMP],1            ; should music player be called?
  452.         jne     @@noplay
  453.  
  454.         call    [dword modPlay] LANG    ; call music player
  455.         test    ax,ax
  456.         jnz     @@plerr
  457.  
  458.     les    si,[sdev]
  459.     cmp    [es:si+SoundDevice.tempoPoll],0     ; poll again if
  460.     je    @@play            ; tempoPoll flag is zero
  461.  
  462. @@noplay:
  463.     cmp    [useEMS],1
  464.     jne    @@nems1
  465.     SetBorder 14
  466.     call    emsStopSafe LANG
  467.         test    ax,ax
  468.         jnz     @@plerr
  469.  
  470.     call    emsRestore LANG
  471.         test    ax,ax
  472.         jnz     @@plerr
  473.  
  474. @@nems1:
  475.     SetBorder 0
  476.  
  477.     les    si,[sdev]
  478.     cmp    [es:si+SoundDevice.tempoPoll],1     ; no need to change
  479.     jne    @@pl1            ; timer rate if tempoPoll is zero.
  480.  
  481.     cmp    [playTmrCount],edi    ; has player timer count been changed?
  482.     je    @@pl1
  483.  
  484.     mov    ebx,[playTmrCount]
  485.     mov    [playCount],ebx     ; set new player timer count
  486.     cmp    [tmrState],tmrPlayer    ; would next interrupt be player?
  487.     jne    @@pl1
  488.     call    nextTimer        ; if so, set new count
  489.  
  490.         jmp     @@pl1
  491.  
  492. @@plerr:
  493.         mov     [plError],ax            ; playing error
  494.  
  495. @@pl1:    mov    [plTimer],0        ; player timer finished
  496.  
  497.  
  498. @@chksys:                ; check system timer
  499.     sti
  500.     cmp    [sysTmrCount],10000h    ; should system timer be called?
  501.     jb    @@done
  502.  
  503.     mov    eax,[sysTmrCount]
  504.     sub    eax,10000h        ; substract 65536 from system timer
  505.     or    eax,eax         ; count. Is the result negative?
  506.     jns    @@stcok         ; (SHOULD not be)
  507.     xor    eax,eax         ; negative - set to zero
  508. @@stcok:
  509.     mov    [sysTmrCount],eax    ; new timer count
  510.     pushf
  511.     call    [dword systemTimer]    ; call system timer
  512.     jmp    @@chksys
  513.  
  514.  
  515.  
  516. @@systmr:                ; system timer only
  517.     sti
  518.     xor    bx,bx            ; set new timer count and restart
  519.     call    setCount
  520.     pushf
  521.     call    [dword systemTimer]    ; call system timer
  522.  
  523.  
  524. @@done:
  525.         SetBorder 0
  526.         pop     gs fs es ds             ; restore registers
  527.     popad
  528.         nop                             ; avoid the popad-bug...
  529.     iret
  530. ENDP
  531.  
  532.  
  533.  
  534.  
  535. ;/***************************************************************************\
  536. ;*
  537. ;* Function:     int tmrGetScrSync(ushort *scrSync);
  538. ;*
  539. ;* Description:  Calculates the screen synchronization value for timer
  540. ;*
  541. ;* Input:        ushort *scrSync         pointer to screen synchronization
  542. ;*                                       value
  543. ;*
  544. ;* Returns:      MIDAS error code.
  545. ;*               Screen syncronization value used with tmrSyncScr() is stored
  546. ;*               in *scrSync.
  547. ;*
  548. ;\***************************************************************************/
  549.  
  550. PROC    tmrGetScrSync   FAR     PscrSync : dword
  551. LOCAL    tmrVal : word
  552.  
  553.     cli                ; disable interrupts for maximum
  554.                     ; accuracy
  555. @@read:
  556.     WaitNextVR            ; wait for next Vertical Retrace
  557.  
  558.     mov    al,36h
  559.     out    43h,al
  560.     xor    al,al            ; reset the timer
  561.     out    40h,al
  562.     out    40h,al
  563.  
  564.  
  565.     WaitNextVR            ; wait for next Vertical Retrace
  566.  
  567.     xor    al,al
  568.     out    43h,al
  569.     in    al,40h
  570.     mov    ah,al
  571.     in    al,40h            ; read timer count - time between
  572.     xchg    al,ah            ; two Vertical Retraces
  573.     neg    ax
  574.     mov    [tmrVal],ax
  575.  
  576.  
  577.     WaitNextVR            ; wait for next Vertical Retrace
  578.  
  579.     mov    al,36h
  580.     out    43h,al
  581.     xor    al,al            ; reset timer again
  582.     out    40h,al
  583.     out    40h,al
  584.  
  585.  
  586.     WaitNextVR            ; wait...
  587.  
  588.     xor    al,al
  589.     out    43h,al
  590.     in    al,40h
  591.     mov    ah,al            ; and read the timer count again
  592.     in    al,40h
  593.     xchg    al,ah
  594.     neg    ax
  595.  
  596.     mov    dx,ax
  597.  
  598.     sub    dx,[tmrVal]
  599.     cmp    dx,2            ; If the difference between the two
  600.     jg    @@read            ; values read was >2, read again.
  601.     cmp    dx,-2
  602.     jl    @@read
  603.  
  604.         sti                             ; enable interrupts
  605.  
  606.         les     bx,[PscrSync]           ; store time in *scrSync
  607.         mov     [es:bx],ax
  608.  
  609.         xor     ax,ax                   ; success
  610.  
  611.     ret
  612. ENDP
  613.  
  614.  
  615.  
  616.  
  617. ;/***************************************************************************\
  618. ;*
  619. ;* Function:     int tmrInit(void);
  620. ;*
  621. ;* Description:  Initializes TempoTimer.
  622. ;*
  623. ;* Returns:      MIDAS error code
  624. ;*
  625. ;\***************************************************************************/
  626.  
  627. PROC    tmrInit     FAR
  628.  
  629.     mov    [tmrState],tmrSystem    ; only system timer now
  630.     mov    [playMusic],0
  631.     mov    [scrSync],0
  632.     mov    [plTimer],0
  633.     mov    [scrTimer],0
  634.     mov    [sysTmrCount],0
  635.     mov    [sysTimer],0
  636.         mov     [plError],0
  637.  
  638.     mov    ax,3508h
  639.     int    21h
  640.     mov    [word systemTimer],bx    ; save system timer interrupt
  641.     mov    [word systemTimer+2],es
  642.  
  643.     push    ds
  644.     mov    ax,seg timer
  645.     mov    ds,ax            ; set new timer interrupt
  646.     mov    dx,offset timer
  647.     mov    ax,2508h
  648.     int    21h
  649.     pop    ds
  650.  
  651.     xor    bx,bx            ; set timer count and restart
  652.     call    setCount
  653.  
  654.         SetBorder 2
  655.  
  656.         xor     ax,ax                   ; success
  657.     ret
  658. ENDP
  659.  
  660.  
  661.  
  662.  
  663. ;/***************************************************************************\
  664. ;*
  665. ;* Function:     int tmrClose(void);
  666. ;*
  667. ;* Description:  Uninitializes TempoTimer. MUST be called if and ONLY if
  668. ;*               tmrInit() has been called.
  669. ;*
  670. ;* Returns:      MIDAS error code
  671. ;*
  672. ;\***************************************************************************/
  673.  
  674. PROC    tmrClose    FAR
  675.  
  676.     mov    al,36h            ; DOS default timer mode
  677.     out    43h,al
  678.     xor    al,al            ; set timer count to 65536 - 18.2Hz
  679.     out    40h,al            ; (DOS default)
  680.     out    40h,al
  681.  
  682.     push    ds
  683.     mov    ax,2508h
  684.     mov    dx,[word systemTimer]    ; restore system timer interrupt
  685.     mov    ds,[word systemTimer+2]
  686.     int    21h
  687.     pop    ds
  688.  
  689.     mov    al,36h            ; DOS default timer mode
  690.     out    43h,al
  691.     xor    al,al            ; set timer again for safety
  692.     out    40h,al
  693.     out    40h,al
  694.  
  695.         xor     ax,ax                   ; success
  696.     ret
  697. ENDP
  698.  
  699.  
  700.  
  701.  
  702. ;/***************************************************************************\
  703. ;*
  704. ;* Function:     int tmrPlay(void (*play)(), SoundDevice *SD);
  705. ;*
  706. ;* Description:  Starts playing music with the timer. Update rate set to 50Hz.
  707. ;*
  708. ;* Input:        void (*play)()          Music playing function
  709. ;*               SoundDevice *SD         Sound Device used for playing
  710. ;*
  711. ;* Returns:      MIDAS error code
  712. ;*
  713. ;\***************************************************************************/
  714.  
  715. PROC    tmrPlay         FAR     play : dword, SD : dword
  716. USES    si
  717.  
  718.     mov    eax,[play]        ; save player routine pointer
  719.     mov    [modPlay],eax
  720.         mov     eax,[SD]                ; save Sound Device pointer
  721.     mov    [sdev],eax
  722.  
  723.     cli                ; disable interrupts for a while
  724.  
  725.     les    si,[sdev]
  726.     cmp    [es:si+SoundDevice.tempoPoll],1
  727.     je    @@tempo         ; use tempo-polling?
  728.  
  729.     cmp    [scrSync],0        ; synchronize to screen?
  730.     je    @@noss
  731.     mov    eax,25            ; yes - synchronize also player
  732.     mul    [scrTmrCount]        ; interrupt count = 1/4 of screen
  733.     mov    ebx,100         ; interrupt count (player interrupt
  734.     div    ebx            ; will come somewhat after Vertical
  735.     mov    ebx,eax         ; Retrace end)
  736.     mov    [scrPlayer],1        ; synchronize player to screen
  737.     jmp    @@1
  738.  
  739. @@noss:
  740.     mov    ebx,1193180/NTPRATE    ; set polling rate to NTPRATE Hz
  741.                     ; (default 100Hz)
  742.     mov    [scrPlayer],0        ; don't synchronize to screen
  743.     jmp    @@1
  744. @@tempo:
  745.         mov     ebx,1193180/50          ; tempo-polling - set update rate to
  746.         mov     [scrPlayer],0           ; 50Hz, don't synchronize to screen
  747. @@1:
  748.     mov    [playTmrCount],ebx    ; player timer count
  749.     mov    [playCount],ebx
  750.         mov     [playMusic],1           ; playing music
  751.         mov     [plTimer],0             ; player timer not active
  752.         mov     [plError],0             ; no error during playing
  753.  
  754.     cmp    [tmrState],tmrSystem    ; is only system timer running?
  755.     jne    @@noset         ; if not, don't set count and restart
  756.  
  757.     mov    [tmrState],tmrPlayer    ; next interrupt will be player int
  758.     call    setCount
  759.     mov    [sysTmrCount],0
  760.  
  761. @@noset:
  762.     sti
  763.  
  764.         SetBorder 3
  765.  
  766.         xor     ax,ax                   ; success
  767.     ret
  768. ENDP
  769.  
  770.  
  771.  
  772.  
  773. ;/***************************************************************************\
  774. ;*
  775. ;* Function:     int tmrStop(void);
  776. ;*
  777. ;* Description:  Stops playing music with the timer.
  778. ;*
  779. ;* Returns:      MIDAS error code
  780. ;*
  781. ;\***************************************************************************/
  782.  
  783. PROC    tmrStop     FAR
  784.  
  785.     cli
  786.  
  787.     mov    [playMusic],0
  788.     cmp    [scrSync],0        ; is timer synchronized to screen?
  789.     jne    @@noset         ; if is, don't force system timer
  790.  
  791.     mov    [tmrState],tmrSystem    ; only system timer now
  792.     mov    [playMusic],0
  793.     xor    bx,bx
  794.     call    setCount
  795.  
  796. @@noset:
  797.     sti
  798.  
  799.         xor     ax,ax                   ; success
  800.     ret
  801. ENDP
  802.  
  803.  
  804.  
  805.  
  806. ;/***************************************************************************\
  807. ;*
  808. ;* Function:     int tmrSyncScr(ushort sync, void (*preVR)(), void (*immVR)(),
  809. ;*               void (*inVR)());
  810. ;*
  811. ;* Description:  Synchronizes the timer to screen refresh.
  812. ;*
  813. ;* Input:        ushort sync             Screen synchronization value returned
  814. ;*                                       by tmrGetScrSync().
  815. ;*               void (*preVR)()         Pointer to the routine that will be
  816. ;*                                       called BEFORE Vertical Retrace
  817. ;*               void (*immVR)()         Pointer to the routine that will be
  818. ;*                                       called immediately after Vertical
  819. ;*                                       Retrace starts
  820. ;*               void (*inVR)()          Pointer to the routine that will be
  821. ;*                                       called some time during Vertical
  822. ;*                                       Retrace
  823. ;*
  824. ;* Returns:      MIDAS error code
  825. ;*
  826. ;* Notes:        preVR() and immVR() functions must be as short as possible
  827. ;*               and do nothing else than update counters or set some VGA
  828. ;*               registers to avoid timer synchronization problems. inVR()
  829. ;*               can take a longer time and can be used for, for example,
  830. ;*               setting the palette.
  831. ;*
  832. ;\***************************************************************************/
  833.  
  834. PROC    tmrSyncScr      FAR     sync : word, PpreVR : dword, PimmVR : dword, \
  835.                                 PinVR : dword
  836. USES    si
  837.  
  838.     cli                ; make sure we won't be disturbed...
  839.  
  840.         mov     eax,[PpreVR]
  841.     mov    [preVR],eax
  842.         mov     eax,[PimmVR]            ; store function pointers
  843.         mov     [immVR],eax
  844.         mov     eax,[PinVR]
  845.         mov     [inVR],eax
  846.  
  847.     mov    [scrSync],1        ; synchronize to screen
  848.     mov    [scrTimer],0        ; screen timer is not active
  849.  
  850.     mov    ax,FRAMETIME
  851.     mul    [sync]            ; time between two screen interrupts
  852.     mov    bx,1000         ; is FRAMETIMER/10 % of total frame
  853.     div    bx            ; time
  854.     movzx    eax,ax
  855.  
  856.     shr    eax,1
  857.     mov    [scrCount],eax        ; screen timer counter
  858.     mov    [scrTmrCount],eax
  859.     mov    ebx,eax
  860.  
  861.     movzx    eax,[sync]
  862.     shr    eax,1            ; scrPVCount = timer count between
  863.     sub    eax,ebx         ; interrupt and start of Vertical
  864.     mov    [scrPVCount],eax    ; Retrace
  865.  
  866.     mov    [tmrState],tmrScreen    ; next timer interrupt is screen timer
  867.     WaitNextVR            ; wait for next retrace
  868.     call    setCount        ; set count and restart timer
  869.  
  870.     sti
  871.  
  872.  
  873.     cmp    [playMusic],0        ; is music being played?
  874.     je    @@nomsync
  875.  
  876.     les    si,[sdev]            ; do not synchronize player
  877.     cmp    [es:si+SoundDevice.tempoPoll],1 ; interrupt to screen if
  878.     je    @@nomsync            ; tempo-polling is used
  879.  
  880.     mov    eax,25
  881.     mul    [scrTmrCount]        ; interrupt count = 1/4 of screen
  882.     mov    ebx,100         ; interrupt count (player interrupt
  883.     div    ebx            ; will come somewhat after Vertical
  884.     mov    ebx,eax         ; Retrace end)
  885.     mov    [scrPlayer],1        ; synchronize player to screen
  886.  
  887.     mov    [playTmrCount],ebx    ; player timer count
  888.     mov    [playCount],ebx
  889.  
  890. @@nomsync:
  891.         xor     ax,ax                   ; success
  892.     ret
  893. ENDP
  894.  
  895.  
  896.  
  897.  
  898. ;/***************************************************************************\
  899. ;*
  900. ;* Function:     int tmrStopScrSync(void);
  901. ;*
  902. ;* Description:  Stops synchronizing the timer to the screen.
  903. ;*
  904. ;* Returns:      MIDAS error code
  905. ;*
  906. ;\***************************************************************************/
  907.  
  908. PROC    tmrStopScrSync    FAR
  909.  
  910.     cli
  911.  
  912.     cmp    [scrPlayer],1        ; is player being synchronized to
  913.     jne    @@nospl         ; screen?
  914.  
  915.     mov    ebx,1193180/NTPRATE    ; set polling rate to NTPRATE Hz
  916.                     ; (default 100Hz)
  917.     mov    [playTmrCount],ebx    ; player timer count
  918.     mov    [playCount],ebx
  919.     mov    [scrPlayer],0        ; don't synchronize to screen
  920.  
  921. @@nospl:
  922.     mov    [scrSync],0        ; no screen synchronization
  923.     mov    [scrTimer],0        ; screen timer is not active
  924.     call    nextTimer        ; set timer count and restart
  925.  
  926.     sti
  927.  
  928.         xor     ax,ax                   ; success
  929.     ret
  930. ENDP
  931.  
  932.  
  933.  
  934.  
  935. ;/***************************************************************************\
  936. ;*
  937. ;* Function:     int tmrSetUpdRate(ushort updRate);
  938. ;*
  939. ;* Description:  Sets the timer update rate, ie. the rate at which the music
  940. ;*               playing routine is called
  941. ;*
  942. ;* Input:        ushort updRate          updating rate, in 100*Hz (5000=50Hz)
  943. ;*
  944. ;* Returns:      MIDAS error code
  945. ;*
  946. ;\***************************************************************************/
  947.  
  948. PROC    tmrSetUpdRate    FAR    updRate : word
  949.  
  950.     les    bx,[sdev]
  951.     cmp    [es:bx+SoundDevice.tempoPoll],0     ; don't change rate
  952.     je    @@done                    ; if tempoPoll == 0
  953.  
  954.     mov    eax,119318000
  955.     cdq                ; eax = new timer count
  956.     movzx    ebx,[updRate]
  957.     div    ebx
  958.     mov    [playTmrCount],eax
  959.  
  960. @@done:
  961.         xor     ax,ax                   ; success
  962.     ret
  963. ENDP
  964.  
  965.  
  966.  
  967. END
  968.