This section describes how to use the built-in MIDAS screen synchronized timer under MS-DOS. This information is not relevant in other operating systems.
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.
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 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.
tmrSyncScr() takes as arguments pointers to three separate functions -- preVR(), immVR() and inVR() -- that 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.
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, or otherwise ensure that the compiler will not optimize the frame waiting loop to an infinite one, waiting for a register variable to change.
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
Unlike in some old MIDAS versions, the current timer code no longer crashes under Windows 95. However, proper screen sychronization is simply impossible under Windows 95, due to inadequacies in Windows 95 timer hardware emulation, 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).
If MIDAS is unable to determine 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 warn the user if that is the case:
SetMode(); mGetScrSync(...) SetTextMode(); if ( mSyncScreen != 1 ) { printf("Warning! Unable to synchronize the program to " "screen update!\n\n" "This normally only happens when running under " "Windows 95 or similar system,\nplease consider" " running this program in MS-DOS mode. The " "program will work,\n but the screen update " "will not be as smooth as possible, and there " "may be\nproblems with music playback.\n\n" "Press any key to continue.\n"); getch(); }
To force Win95 compatibility mode, set mSyncScreen to 0 yourself before calling mGetScrSync the first time. This allows you to set the timer run at a constant rate, independent of the display refresh rate.