MIDAS Sound System                   Advanced Topics                    Page 7


3. Advanced Topics

----------------------------------------------------------------------------
3.1. Timer Screen Synchronization
----------------------------------------------------------------------------

3.1.1. Introduction

        The MIDAS Sound System timer includes built-in support for screen
        synchronization. This means that you can program the timer to call
        your own routines every frame, more exactly immediately before the
        Vertical Retrace, immediately after the Vertical Retrace has started
        and later during the Vertical Retrace. This enables you to synchronize
        your program to the screen update to get smooth animation, which
        otherwise would not be possible with a music system playing in the
        background.  The routines can also be used for changing VGA hardware
        registers, such as display start address and scrolling, in correct
        moments, for triple buffering and for compensating for different
        machine speeds.

3.1.2. Using Screen Synchronization

        If you wish to use timer screen synchronization, use the procedure
        outlined below:

        1. BEFORE MIDAS Sound System is initialized, set up the display mode
        you are intending to use and get the timer screen synchronization
        value corresponding to that display mode using tmrGetScrSync(). If you
        are using several display modes with different refresh rates (in
        practise, different vertical resolutions, although in standard VGA
        only 240 or 480 scan line modes have different refresh rates) you must
        activate each of them in turn and get the synchronization values for
        each of them.

        2. Initialize MIDAS Sound System etc.

        3. Set up the display mode

        4. When you need timer screen synchronization, start it using the
        function tmrSyncScr(), passing as arguments the timer screen
        synchronization value from step 1 and pointers to the routines you
        wish the timer to call. If you do not require support for some
        routine, pass a NULL pointer (dword 0) instead.

        5. When timer screen synchronization is no longer required, stop it
        using tmrStopScrSync().

        If you change the display mode to one with a different refresh rate,
        you must first stop the screen synchronization, change the display
        mode, and after that re-synchronize the timer. Please note that
        synchronizing the timer to the screen update takes a while, and as the
        timer is disabled for that time it may introduce breaks in the music.
        Therefore we suggest you handle the timer screen synchronization
        before you start playing music.

3.1.3. Timer Screen Synchronized Routines

        tmrSyncScr() takes as arguments pointers to three separate functions,
        preVR(), immVR() and inVR(), which will be called at different points
        during the screen timer interrupt. Following is a brief description of
        each routine and what it is intended for.

        preVR() is called immediately before Vertical Retrace, and must be as
        short as possible to avoid timer synchronization problems. It is
        intended mainly for changing the display start address register and
        updating counters.

        immVR() is called immediately after Vertical Retrace has started, and
        must be as short as possible to avoid timer synchronization problems.
        It is intended mainly for changing VGA hardware registers that have to
        be modified during Vertical Retrace, such as pixel panning.

        inVR() is called after immVR(), and may take a longer time if
        necessary. However, note that even though spending a long time in
        inVR() does not induce timer synchronization problems, it may cause
        problems in music tempo if it takes a too long time. Furthermore, the
        time spent in inVR() must not exceed one frame. inVR() is mainly
        intended for changing the palette or updating small portions of
        screen, such as drawing new characters to a start address scroller.

3.1.4. Waiting for Vertical Retrace

        When synchronizing your program to the screen update, instead of
        waiting for Vertical Retrace using the VGA hardware registers you must
        use the screen synchronized timer for this. This is because the music
        playing interrupt may occur just during the Vertical Retrace, causing
        you to miss one frame completely. To use the timer for this, set up a
        preVR() routine that increments a frame counter, and instead of
        waiting for Vertical Retrace bit wait for the frame counter to change.
        For example:

        preVR:
                frameCount = frameCount + 1

        main:
                ...
                tmrSyncScr(scrSync, &preVR, NULL, NULL)
                ...
                oldCnt = frameCount
                while oldCnt == frameCount;

        Note that you must declare frameCount as "volatile" (in C) or
        otherwise ensure that the compiler will not optimize the frame waiting
        loop to an infinite one, waiting for a register variable to change.

3.1.5. Speed Compensation

        The timer screen synchronization can also be used to compensate for
        different speeds on different computers. The following pseudo code
        should illustrate the point:

        main loop:
                Wait for frameCount to change
                skipFrames = oldFrameCount - frameCount
                oldFrameCount = frameCount
                for i = 1 to skipFrames do
                        MoveEverything
                DrawEverything

3.1.6. Implementation notes

        The new timer code in m32abd -release no longer crashes under
        Windows 95. However, proper screen sychronization is simply
        impossible under Windows 95, and MIDAS will just set up a
        steady timer of 70Hz (or what ever refresh rate you set) and
        call the display synchronization routines there.

        For most purposes, this should present no problems to you. The
        display update might not be as smooth as under plain DOS, but
        no DOS programs run absolutely smoothly under Win95 anyway and
        the users should be used to that. However, if you use the
        screen synchronized timer for page flipping, you'll need to
        change from double to triple buffering. The reason for this is,
        that you can no longer be sure that the new page is indeed
        displayed after the timer interrupt that changed the start
        address returns. VGA will make use of the start address only at
        the next Vertical Retrace, and there is no guarantee that the
        timer comes at Vertical Retrace time (in fact it usually
        doesn't). This won't crash your system, just look ugly, and if
        you don't care about Win95 users just forget about this -
        flickering is better than crashing anyway.

        If MIDAS is unable to get the correct display sync value (ie.
        the frame rate), it will use the frame rate set in the variable
        mDefaultFramerate (in 100*Hz => 70Hz becomes 7000). This
        variable is initially set to 7000 (70Hz), but if you know the
        refresh rate of your display mode is different you can change
        this before calling mGetScrSync. If your application uses
        several display modes with different frame rates, just set
        mDefaultFramerate before reading the sync value for each of
        them as follows:

                SetFirstMode();
                mDefaultFrameRate = 6000; /* 60 Hz */
                mGetScrSync(...)
                SetSecondMode()
                mDefaultFrameRate = 5000; /* 50 Hz */
                mGetScrSync(...)

        To check whether MIDAS was able to synchronize to the screen
        correctly, check the variable mSyncScreen after calling
        mGetScrSync(). If the variable is 0, MIDAS was unable to
        determine the frame rate, took it from mDefaultFramerate, and
        is now running in Win95 compatibility mode. You'll probably
        want to complain to the user if that is the case:

                SetMode();
                mGetScrSync(...)
                SetTextMode();
                if ( mSyncScreen != 1 )
                {
                    printf("Unable to sync to screen - you are "
                           "probably running under Win95.\n"
                           "The program will NOT work well and you \n"
                           "may experience problems in music and "
                           "flickering.\n"
                           "Press a key to continue\n");
                    getch();
                }

        To force Win95-mode you can set mSyncScreen to 0 yourself
        before calling mGetScrSync the first time.

        Note that basically all of the discussion here applies only to
        running under Win95. Under plain DOS everything should work as
        before.