home *** CD-ROM | disk | FTP | other *** search
/ HomeWare 14 / HOMEWARE14.bin / music / vaisdk.arj / VESA.C < prev    next >
C/C++ Source or Header  |  1994-04-10  |  43KB  |  1,603 lines

  1.  
  2. ;   /*\
  3. ;---|*|----====< VESA >====----
  4. ;---|*|
  5. ;---|*| high level support routines for the VESA AI interface
  6. ;---|*|
  7. ;---|*| Copyright (c) 1993,1994  V.E.S.A, Inc. All Rights Reserved.
  8. ;---|*|
  9. ;---|*| Portions Copyright (c) 1993 Jason Blochowiak, Argo Games. Used
  10. ;---|*| with permission.
  11. ;---|*|
  12. ;---|*| VBE/AI 1.0 Specification
  13. ;---|*|    February 2, 1994. 1.00 release
  14. ;---|*|
  15. ;   \*/
  16.  
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <direct.h>
  20. #include <string.h>
  21. #include <dos.h>
  22. #include <io.h>
  23. #include <conio.h>
  24.  
  25. #include "vbeai.h"
  26. #include "vesa.h"
  27.  
  28. #define TRUE    -1
  29. #define FALSE    0
  30. #define ON      TRUE
  31. #define OFF     FALSE
  32.  
  33. #if DEBUG
  34. #define BREAKPOINT _asm{int 3};
  35. #else
  36. #define BREAKPOINT _asm{nop};
  37. #endif
  38.  
  39. ;   /*\
  40. ;---|*| Global Variables
  41. ;   \*/
  42.  
  43.         // nada...
  44.  
  45. ;   /*\
  46. ;---|*| Local Variables
  47. ;   \*/
  48.  
  49. #define CALLBACKS       16  // Maximum number of callbacks that can be installed
  50. #define MAXNESTEDCBS    3   // Maximum nesting of timer interrupts
  51.  
  52.         typedef struct {
  53.             VESAHANDLE  handle;                         // The device handle
  54.             void        (far pascal *callBack)(void);   // The callback function
  55.             short       semaphore;                      // Our lockout semaphore for this callback
  56.             long        divisor;                        // The timer divisor this callback wants
  57.             long        divCount;                       // Overflow counter for this callback
  58.         } Timer_t;
  59.  
  60.     // timer callback control table
  61.  
  62.         static volatile Timer_t timers[CALLBACKS];
  63.  
  64.     // a table to store data from device info blocks
  65.  
  66.         static int  doreg[CALLBACKS][2]    =      // do give ticks to this dev
  67.             {{0,0},{0,0},{0,0},{0,0},
  68.              {0,0},{0,0},{0,0},{0,0},
  69.              {0,0},{0,0},{0,0},{0,0},
  70.              {0,0},{0,0},{0,0},{0,0}};
  71.  
  72.         static int  denom      = 18;            // decimation denominator
  73.         static int  isopen     = 0;             // number of devices opened
  74.  
  75.         static int  timerhooked = FALSE;        // TRUE if timer is currently hooked
  76.         static int  curnestedtimer = 0;         // Number of nested timer interrupts
  77.         static long curtimerdiv = 0x10000L;     // Current hardware timer divisor
  78.         static long curtimerslop = 0;           // Number of timer counts that we've missed, due to excessive nesting
  79.  
  80.     // general prototypes
  81.  
  82.         static int  cmpstr                      ( char far *, char far *, int );
  83.         static int  readblock                   ( int, char huge *, int );
  84.  
  85.     // timer prototypes
  86.  
  87.         static int  findhandleindex ( VESAHANDLE han );
  88.         static void setuptimer      ( void );
  89.         static void programtimer    ( unsigned );
  90.         static void interrupt ourintvect ( void );
  91.  
  92.     // patch handling prototypes
  93.  
  94.         static void pascal FreePatchBlock       ( void far * );
  95.         static void far * pascal GetPatchBlock  ( int );
  96.         static VAIDhdr far *pascal GetPatch     ( VESAHANDLE, int );
  97.         static long pascal GetPatchLength       ( VESAHANDLE, int );
  98.         static void far pascal OurFreeCBCallBack( VESAHANDLE, int, void far *, long );
  99.  
  100.     // patch handler data structures
  101.  
  102.         typedef struct {
  103.             int  han;                   // VBE/AI handle
  104.             fpMIDServ ms;               // far pointer to the services structure
  105.             long    poffset;            // offset into the file to VAIP
  106.             FILE   *fp;                 // pointer to the patch file
  107.             VAIIhdr (far *fpVAII)[];    // far pointer to the index portion
  108.             VAIDhdr (far *fpVAID)[];    // far pointer to the data portion, if loaded
  109.         } PatchCTL;
  110.  
  111. #define MAXLIBS     4
  112. #define VAIIPLEN    (sizeof(VAIIhdr)*256)
  113.         static PatchCTL PatchLibs[MAXLIBS] = // static of libraries can be
  114.             { 0,0,0,0,0,0,
  115.               0,0,0,0,0,0,
  116.               0,0,0,0,0,0,
  117.               0,0,0,0,0,0
  118.             };
  119.  
  120.     // the table of Patch blocks for use by the MIDI drivers. This is a
  121.     // global pool for all MIDI drivers.
  122.  
  123. #define MAXFREE     64                      // maximum free blocks we save
  124.         static int       FreeMax = 0;       // last free position
  125.         static void far *PatchPtrs[MAXFREE];
  126.         static long      PatchLength[MAXFREE];
  127.         static int       PatchState[MAXFREE] =  // TRUE=inuse, FALSE=available
  128.             { 0 };
  129.  
  130. ;
  131. ;   /*\
  132. ;---|*|------------------==============================-------------------
  133. ;---|*|------------------====< Start of execution >====-------------------
  134. ;---|*|------------------==============================-------------------
  135. ;   \*/
  136.  
  137. ;
  138. ;   /*\
  139. ;---|*|----====< AllocateBuffer >====----
  140. ;---|*|
  141. ;---|*| Allocate some huge memory from the DOS pool
  142. ;---|*|
  143. ;   \*/
  144. void far * pascal AllocateBuffer(siz)
  145.     long siz;
  146. {
  147. void far *f;
  148.  
  149.     // go to DOS to get the memory block size
  150.  
  151.         _asm {
  152.  
  153.         // get the # of paragraphs into BX
  154.  
  155.             mov     bx,word ptr [siz+0] // 0xLLLL
  156.             mov     dx,word ptr [siz+2] // 0x000H
  157.             shr     bx,4            // 0x0LLL
  158.             shl     dx,12           // 0xH000
  159.             add     bx,dx           // 0xHLLL: bx = 16 bits out of 20 bits
  160.             inc     bx              // get one more 16 byte block
  161.             inc     bx              // get one more 16 byte block
  162.  
  163.         // call DOS for a memory block
  164.  
  165.             mov ah,48h
  166.             int 21h
  167.  
  168.         // carry is set if in error.
  169.  
  170.             cmc                     // set carry if no error
  171.             sbb     bx,bx
  172.             and     ax,bx           // ax = 0 if in error, else a segment
  173.             sub     dx,dx
  174.             xchg    ax,dx           // dx:ax holds the pointer
  175.  
  176.             mov     word ptr [f+0],ax
  177.             mov     word ptr [f+2],dx
  178.         }
  179.  
  180.     // return the modified, or empty pointer
  181.  
  182.         return(f);
  183. }
  184.  
  185. ;
  186. ;   /*\
  187. ;---|*|----====< FreeBuffer >====----
  188. ;---|*|
  189. ;---|*| Allocate some huge memory from the DOS pool
  190. ;---|*|
  191. ;   \*/
  192. void pascal FreeBuffer(void far *p)
  193. {
  194. void far *f;
  195.  
  196.     // go to DOS to release the memory block size
  197.  
  198.         _asm {
  199.  
  200.         // call DOS for a memory block
  201.  
  202.             mov es,word ptr [p+2]
  203.             mov ah,49h
  204.             int 21h
  205.  
  206.         }
  207. }
  208.  
  209. ;
  210. ;   /*\
  211. ;---|*|----====< VESAFindADevice >====----
  212. ;---|*|
  213. ;---|*| This function returns the next available device handle
  214. ;---|*| from the driver list.
  215. ;---|*|
  216. ;   \*/
  217.  
  218. VESAHANDLE pascal VESAFindADevice ( class )
  219.     int class;
  220. {
  221. static VESAHANDLE lh = 0;
  222.  
  223.     // if this is a reset call, do it...
  224.  
  225.         if (class == -1)
  226.             return(lh = 0); // start over...
  227.  
  228.     // call the driver directly
  229.  
  230.         _asm {
  231.             mov     ax,VESAFUNCID
  232.             mov     bx,VESAFUNC1
  233.             mov     cx,[lh]
  234.             mov     dx,[class]
  235.             int     INTHOOK
  236.             mov     [lh],cx
  237.         }
  238.  
  239.     // return the finding
  240.  
  241.         return (lh);
  242.  
  243. }
  244.  
  245. ;
  246. ;   /*\
  247. ;---|*|----====< VESAQueryDevice >====----
  248. ;---|*|
  249. ;---|*| Query the driver for general or device specific data.
  250. ;---|*|
  251. ;   \*/
  252. long pascal VESAQueryDevice ( han, query, ptr )
  253.     VESAHANDLE han;
  254.     int        query;
  255.     void far  *ptr;
  256. {
  257. long cc;
  258. int ticks,n;
  259.  
  260.     // ask the driver for some specific information
  261.  
  262.         cc = (long) ptr; // we have to do this due to MS bugs in inline code!
  263.  
  264.         _asm {
  265.             push    si
  266.             push    di
  267.  
  268.             mov     ax,VESAFUNCID
  269.             mov     bx,VESAFUNC2
  270.             mov     cx,[han]
  271.             mov     dx,[query]
  272.  
  273.             mov     si,word ptr [cc+2]
  274.             mov     di,word ptr [cc+0]
  275.  
  276.             int     INTHOOK
  277.  
  278.             sub     ax,004Fh        // considered a good return
  279.             neg     ax              // null out SI:DI if AX != 0x004F
  280.             sbb     ax,ax
  281.             not     ax
  282.             and     si,ax
  283.             and     di,ax
  284.  
  285.             mov     word ptr [cc+2],si
  286.             mov     word ptr [cc+0],di
  287.  
  288.             pop     di
  289.             pop     si
  290.         }
  291.  
  292.     // if just a length query, return the value now
  293.  
  294.         if (query == VESAQUERY1)
  295.             return (cc);
  296.         if (query == VESAQUERY3)
  297.             return (cc);
  298.         if (query == VESAQUERY5)
  299.             return (cc);
  300.  
  301.     // if this is returning the info block, grab the timer tick count
  302.     // since this is the only time we may see the INFO block.
  303.  
  304.         if (query == VESAQUERY2) {
  305.  
  306.             switch (((fpGDC)cc)->gdclassid) {
  307.  
  308.                 case WAVDEVICE:
  309.                     ticks = ((fpGDC)cc)->u.gdwi.witimerticks;
  310.                     break;
  311.  
  312.                 case MIDDEVICE:
  313.                     ticks = ((fpGDC)cc)->u.gdmi.mitimerticks;
  314.                     break;
  315.  
  316.                 default:
  317.                 case VOLDEVICE:
  318.                     ticks = 0;  // no timer ticks for these types
  319.                     break;
  320.             }
  321.  
  322.             // see if the handle is already registered, if so, we're done
  323.  
  324.             for (n=1;n<CALLBACKS;n++) {
  325.                 if (doreg[n][0] == han) {
  326.                     ticks = 0;
  327.                     break;
  328.                 }
  329.             }
  330.  
  331.             // if timer ticks needed, save that fact for a later open
  332.  
  333.             if (ticks) {
  334.                 for (n=1;n<CALLBACKS;n++) {
  335.                     if (!doreg[n][0]) {
  336.                         doreg[n][0] = han;
  337.                         doreg[n][1] = ticks;
  338.                         break;
  339.                     }
  340.                 }
  341.             }
  342.         }
  343.  
  344.     // return the completion code
  345.  
  346.         return (cc);
  347.  
  348. }
  349.  
  350. ;
  351. ;   /*\
  352. ;---|*|----====< VESAOpenADevice >====----
  353. ;---|*|
  354. ;---|*| This function returns a pointer to the driver
  355. ;---|*| services table if the driver opens successfully.
  356. ;---|*|
  357. ;   \*/
  358. void far *pascal VESAOpenADevice ( han, apiset, memptr )
  359.     VESAHANDLE han;
  360.     int apiset;
  361.     void far *memptr;
  362. {
  363. void far *cc = 0;
  364. int n;
  365.  
  366.     // attempt to open the device
  367.  
  368.         _asm {
  369.             cmp     word ptr [memptr+0],0   // offset must be null
  370.             jnz     voad_05
  371.  
  372.             mov     ax,VESAFUNCID           // open function
  373.             mov     bx,VESAFUNC3
  374.             mov     cx,[han]                // device handle is required
  375.             mov     dx,[apiset]             // select 16/32 bit interface
  376.             mov     si,word ptr [memptr+2]  // pass in the segment
  377.             int     INTHOOK                 // do the interrupt
  378.  
  379.             mov     word ptr [cc+2],si
  380.             mov     word ptr [cc+0],cx
  381.         }
  382.         voad_05:;
  383.  
  384.     // if successful, check to see if the device needs timer ticks
  385.  
  386.         if (cc) {
  387.  
  388.             for (n=1;n<CALLBACKS;n++) {
  389.  
  390.                 // if this is it, setup for timer ticks
  391.  
  392.                 if (doreg[n][0] == han) {
  393.  
  394.                     // register wave audio
  395.  
  396.                     if ( _fstrncmp(((fpWAVServ)cc)->wsname,"WAVS",4) == 0 )
  397.  
  398.                         VESARegisterTimer
  399.                           (
  400.                             han,                            // handle
  401.                             ((fpWAVServ)cc)->wsTimerTick,   // address
  402.                             VESARateToDivisor(doreg[n][1])  // tick count
  403.                           );
  404.  
  405.                     // register midi audio
  406.  
  407.                     if ( _fstrncmp(((fpMIDServ)cc)->msname,"MIDS",4) == 0 )
  408.  
  409.                         VESARegisterTimer
  410.                           (
  411.                             han,                            // handle
  412.                             ((fpMIDServ)cc)->msTimerTick,   // address
  413.                             VESARateToDivisor(doreg[n][1])  // tick count
  414.                           );
  415.  
  416.                     break;
  417.                 }
  418.             }
  419.         }
  420.  
  421.     // return the pointer to the services table
  422.  
  423.         return(cc);
  424.  
  425. }
  426.  
  427. ;
  428. ;   /*\
  429. ;---|*|----====< VESACloseDevice >====----
  430. ;---|*|
  431. ;---|*| This function closes an opened device.
  432. ;---|*|
  433. ;   \*/
  434. void  pascal VESACloseDevice ( han )
  435.     VESAHANDLE han;
  436. {
  437. int n,gd;
  438.  
  439.     // remove the timer callbacks for this handle
  440.  
  441.         VESARegisterTimer( han, 0, 0 );
  442.  
  443.     // make sure MIDI devices free up bank memory
  444.  
  445.         VESAFreePatchBank ( han );
  446.  
  447.     // go close the device.
  448.  
  449.         _asm {
  450.             mov     ax,VESAFUNCID
  451.             mov     bx,VESAFUNC4
  452.             mov     cx,[han]            // if the handle exists, the
  453.             int     INTHOOK             // device will close
  454.         }
  455. }
  456.  
  457. ;
  458. ;   /*\
  459. ;---|*|----====< VESAFreePatchBank >====----
  460. ;---|*|
  461. ;---|*| This function will release the patch bank data memory and
  462. ;---|*| remove the VBE device from the internal tables.
  463. ;---|*|
  464. ;   \*/
  465. int  pascal VESAFreePatchBank ( han )
  466.     VESAHANDLE han; // device handle
  467. {
  468. int n;
  469. int inuse = 0;
  470.  
  471.     for (n=0;n<MAXLIBS;n++) {
  472.  
  473.         // only one will match. Free up the memory, then flush the entry
  474.  
  475.         if (han == PatchLibs[n].han) {
  476.  
  477.             // free the patch libs memory
  478.  
  479.             if (PatchLibs[n].fpVAII)
  480.                 FreeBuffer((void far *)PatchLibs[n].fpVAII);
  481.  
  482.             // free the patch data memory
  483.  
  484.             if (PatchLibs[n].fpVAID);
  485.                 FreeBuffer((void far *)PatchLibs[n].fpVAID);
  486.  
  487.             // close the file
  488.  
  489.             if (PatchLibs[n].fp)
  490.                 fclose(PatchLibs[n].fp);
  491.  
  492.             PatchLibs[n].han    = 0;        // clear out this entry
  493.             PatchLibs[n].ms     = 0;
  494.             PatchLibs[n].fp     = 0;
  495.             PatchLibs[n].fpVAII = 0;
  496.             PatchLibs[n].fpVAID = 0;
  497.             PatchLibs[n].poffset= 0;
  498.  
  499.             break;                          // leave the loop
  500.         }
  501.     }
  502.  
  503.     // see if all banks are unloaded. "inuse" == 0 if no more banks loaded
  504.  
  505.         for (n=0;n<MAXLIBS;n++)
  506.             if (PatchLibs[n].han)
  507.                 inuse++;
  508.  
  509.     // if all patch banks are freed, then flush the MIDI patch blocks
  510.  
  511.         if ((!inuse) && (FreeMax)) {
  512.  
  513.             for (n=0;n<FreeMax;n++) {
  514.  
  515.                 FreeBuffer(PatchPtrs[n]);
  516.  
  517.                 PatchPtrs[n]   = 0;
  518.                 PatchLength[n] = 0;
  519.                 PatchState[n]  = FALSE;
  520.  
  521.             }
  522.         }
  523. }
  524.  
  525. ;
  526. ;   /*\
  527. ;---|*|----====< VESALoadPatchBank >====----
  528. ;---|*|
  529. ;---|*| This function will load the patch bank index structure into memory.
  530. ;---|*| If the data portion is small, then it too will be loaded.
  531. ;---|*|
  532. ;   \*/
  533. int  pascal VESALoadPatchBank ( han, ms, bnk )
  534.     VESAHANDLE han; // device handle
  535.     fpMIDServ ms;   // fp to services structure
  536.     char far *bnk;
  537. {
  538. FILE *fp;
  539. long len,l;
  540. int  n,idx=-1;
  541. char far *pt,far *opt, far *patchbank;
  542. char fname[100],*s,*b;
  543. char *env,c;
  544.  
  545.     // make sure we don't already have this device's bank or are already
  546.     // filled with patch banks
  547.  
  548.         for (c=n=0;n<MAXLIBS;n++) {
  549.  
  550.             if (PatchLibs[n].han == han)    // bail if already here
  551.                 return(FALSE);
  552.  
  553.             if (PatchLibs[n].han != 0)      // count the used slots
  554.                 c++;
  555.  
  556.             if (PatchLibs[n].han == 0)      // record the empty slots
  557.                 if (idx == -1)
  558.                     idx = n;
  559.         }
  560.  
  561.         if (c == MAXLIBS)                   // if all slots full, bail
  562.             return(FALSE);
  563.  
  564.     // open the patch bank in the current directory, PATH or root
  565.  
  566.         _fstrcpy (fname,bnk);
  567.  
  568.         if ((fp = fopen (fname,"rb")) == 0) {
  569.  
  570.             // if there is a path statement, process it...
  571.  
  572.             if ((env = getenv ("PATH")) != 0) {
  573.  
  574.                 s = fname;      // setup to process each string
  575.  
  576.                 while (s) {
  577.  
  578.                     // pull the next string from the path
  579.  
  580.                         c = 0;
  581.                         while (*env)
  582.                             if ((*s++ = c = *env++) == ';')
  583.                                 break;
  584.  
  585.                     // if a semicolon stopped us, point to it...
  586.  
  587.                         if (c == ';') s--;
  588.  
  589.                         *s = 0;             // set a terminator
  590.  
  591.                     // if there is no other string, break out now...
  592.  
  593.                         if (s == fname) {
  594.                             s = 0;
  595.                             continue;
  596.                         }
  597.  
  598.                     // if the prior character is not a backslash, append one
  599.  
  600.                         if (*(s-1) != '\\') {
  601.                             *s++ = '\\';
  602.                             *s = 0;
  603.                         }
  604.  
  605.                     // append the library name now
  606.  
  607.                         b = bnk;
  608.                         while (*s++ = *b++) ;
  609.  
  610.                     // attempt to open the bank now
  611.  
  612.                         if ((fp = fopen (fname,"rb")))
  613.                             break;
  614.  
  615.                         s = fname;      // reset for the next iteration
  616.  
  617.                 }
  618.  
  619.             }
  620.  
  621.             // if no library after the path search, try the root directory
  622.  
  623.             if (!fp) {
  624.  
  625.                 // try the root directory of the current drive
  626.  
  627.                 fname[0] = '@' + _getdrive();   // get the current drive
  628.                 fname[1] = ':';
  629.                 fname[2] = '\\';
  630.                 s = &fname[3];
  631.  
  632.                 b = bnk;
  633.                 while (*s++ = *b++) ;
  634.  
  635.                 fp = fopen (fname,"rb");
  636.  
  637.             }
  638.  
  639.             // if after all that, if not opened, it's not there!
  640.  
  641.             if (!fp)
  642.                 return(FALSE);
  643.  
  644.         }
  645.  
  646.     // allocate memory for the index block
  647.  
  648.         if ((patchbank = AllocateBuffer(VAIIPLEN)) == 0) {
  649.             fclose (fp);
  650.             return(FALSE);
  651.         }
  652.  
  653.     // load in the first chunk. The first "RIFF" block must be here!
  654.  
  655.         len = readblock (fileno(fp),(pt=patchbank),VAIIPLEN) & 0xffff;
  656.  
  657.         if (cmpstr(&((VAILhdr far *)pt)->type[0], "RIFF",4) != 0) {
  658.             fclose (fp);
  659.             return(FALSE);
  660.         }
  661.         pt += sizeof(RIFFhdr);          // skip the header
  662.  
  663.     // load in the first chunk. The first "vail" block must be here!
  664.  
  665.         if (cmpstr(&((VAILhdr far *)pt)->type[0], "vail",4) != 0) {
  666.             fclose (fp);
  667.             return(FALSE);
  668.         }
  669.         pt += sizeof(VAILhdr);          // skip the header
  670.  
  671.     // loop through till the vaii header is located
  672.  
  673.         while (1) {
  674.  
  675.             opt = pt;
  676.  
  677.             if (cmpstr(&((ZSTRhdr far *)pt)->type[0], "ZSTR",4) == 0) {
  678.                 l = ((ZSTRhdr far *)pt)->tlen;
  679.                 pt += sizeof(ZSTRhdr)+l; // skip the ASCIIZ block
  680.             }
  681.  
  682.             if (cmpstr(&((VAIPhdr far *)pt)->type[0], "vaip",4) == 0) {
  683.                 pt += sizeof (VAIPhdr); // move past this block
  684.                 break;                  // we're done, save this pointer!
  685.             }
  686.  
  687.             if (opt == pt) {
  688.                 fclose (fp);
  689.                 return(FALSE);          // invalid file format
  690.             }
  691.         }
  692.  
  693.     // we have the start of the VAII record. reposition the file pointer,
  694.     // then pull in the entire index.
  695.  
  696.         len = (int)pt - (int)patchbank;     // get the starting offset
  697.         fseek (fp,len,SEEK_SET);
  698.  
  699.     // save the starting position of the VAIP record
  700.  
  701.         PatchLibs[idx].poffset= ftell(fp) - sizeof (VAIPhdr);
  702.  
  703.         if (readblock (fileno(fp),patchbank,VAIIPLEN & 0xffff) != VAIIPLEN) {
  704.             fclose (fp);
  705.             return(FALSE);
  706.         }
  707.  
  708.     // register this VESA device in our system
  709.  
  710.         PatchLibs[idx].han    = han;    // record this new entry
  711.         PatchLibs[idx].ms     = ms;
  712.         (void far *)PatchLibs[idx].fpVAII = (void far *)patchbank;
  713.  
  714.     // grab the callback since we're doing the patch handling
  715.  
  716.         ms->msApplFreeCB = &OurFreeCBCallBack;
  717.  
  718.     // get the unused file length to determine if we can preload the patch data
  719.  
  720.         len  = filelength (fileno(fp)); // patch size = file len. - index
  721.         len -= ftell(fp);
  722.  
  723.     // just load banks less than 64K
  724.  
  725.         if (len < 65535) {
  726.  
  727.             // if not enough memory, skip it...
  728.  
  729.                 if ((patchbank = AllocateBuffer(len)) == 0)
  730.                     return(TRUE);
  731.  
  732.             // read in the patch data now. If not, we'll do the slow method
  733.  
  734.                 if (((long)readblock(fileno(fp),patchbank,(int)len) & 0xffff) != len)
  735.                     return(TRUE);
  736.  
  737.             // record the patch data buffer now
  738.  
  739.                 (void far *)PatchLibs[idx].fpVAID = (void far *)patchbank;
  740.                 fclose (fp);
  741.         }
  742.  
  743.     // if >64K, then we need to go to disk for each patch.
  744.  
  745.         else {
  746.  
  747.             // save the file pointer for later access when we need the data
  748.  
  749.             PatchLibs[n].fp = fp;
  750.         }
  751.  
  752.     // return the length of the structure
  753.  
  754.         return(TRUE);
  755.  
  756. }
  757.  
  758. ;
  759. ;   /*\
  760. ;---|*|----====< VESAPreloadPatch >====----
  761. ;---|*|
  762. ;---|*| Download the VESA patch to the driver.
  763. ;---|*|
  764. ;---|*| NOTE: This routine may call DOS, so use it only in the foreground!
  765. ;---|*|
  766. ;   \*/
  767. int  pascal VESAPreLoadPatch ( han, patch, ch )
  768.     VESAHANDLE han; // device handle
  769.     int patch;      // patch # to be downloaded
  770.     int ch;         // channel # for download
  771. {
  772. long isdata;
  773. int n,trim;
  774. char far *pptr;
  775.  
  776.     // percussive patches reside in the second half of the table
  777.  
  778.         if (ch == 9)
  779.             patch |= 0x80;
  780.  
  781.     // get the patch and size of patch
  782.  
  783.         for (n=0;n<MAXLIBS;n++) {
  784.  
  785.             // if a device is found, try to load it's patch
  786.  
  787.             if (PatchLibs[n].han == han) {
  788.  
  789.                 // if the patch is already loaded, we can just do it...
  790.  
  791.                 if (PatchLibs[n].fpVAID) {
  792.  
  793.                     // send off the patch - just the data portion, no VAIDhdr
  794.  
  795.                        (*PatchLibs[n].ms->msPreLoadPatch)
  796.                           (
  797.                             ch,
  798.                             patch & 0x7F,
  799.                             GetPatch       ( han, patch ), // sizeof(VAIDhdr),
  800.                             GetPatchLength ( han, patch )
  801.                           );
  802.  
  803.                         break;  // exit the for loop
  804.                 }
  805.  
  806.                 else {
  807.  
  808.                     // get the length needed
  809.  
  810.                         isdata = GetPatchLength ( han, patch );
  811.                         trim   = sizeof (VAIDhdr);
  812.  
  813.                         while (isdata) {
  814.  
  815.                             // find the size needed
  816.  
  817.                             n = (*PatchLibs[n].ms->msPreLoadPatch)(ch,patch&0x7F,0,0);
  818.  
  819.                             // if we get a block, process the data
  820.  
  821.                             if (pptr = GetPatchBlock(n)) {
  822.  
  823.                                 // read it from disk
  824.  
  825.                                 fseek
  826.                                   (
  827.                                     PatchLibs[n].fp,
  828.                                     PatchLibs[n].poffset+
  829.                                         (*PatchLibs[n].fpVAII)[patch].poffset,
  830.                                     SEEK_SET
  831.                                   );
  832.  
  833.                                 n = readblock (fileno(PatchLibs[n].fp),pptr,n);
  834.  
  835.                                 // send it to the driver, and maybe free it up
  836.  
  837.                                 if (!(*PatchLibs[n].ms->msPreLoadPatch)
  838.                                   (
  839.                                     ch,
  840.                                     patch&0x7F,
  841.                                     pptr+trim,
  842.                                     n
  843.                                   ))
  844.                                     FreePatchBlock(pptr);
  845.  
  846.                                 // remove our portion
  847.  
  848.                                 trim    = 0;
  849.                                 isdata -= n;
  850.                             }
  851.  
  852.                             // oops, no data, let's pull back...
  853.  
  854.                             else {
  855.                                 (*PatchLibs[n].ms->msUnloadPatch) (ch,patch&0x7F);
  856.                                 break;
  857.                             }
  858.  
  859.                         }
  860.  
  861.                     break;  // exit the for loop
  862.                 }
  863.             }
  864.         }
  865. }
  866.  
  867. ;
  868. ;   /*\
  869. ;---|*|----====< VESARateToDivisor >====----
  870. ;---|*|
  871. ;---|*| Converts an integral rate (in Hz) to a timer divisor value, which
  872. ;---|*| can then be passed to VESARegisterTimer().
  873. ;---|*|
  874. ;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission.
  875. ;---|*|
  876. ;   \*/
  877. long pascal VESARateToDivisor(rate)
  878.     long rate;
  879. {
  880. long result;
  881.  
  882.     if (rate)
  883.         result = 1193180L / rate;
  884.     else
  885.         result = 0x10000L;
  886.  
  887.     return(result);
  888. }
  889.  
  890. ;
  891. ;   /*\
  892. ;---|*|----====< VESARegisterTimer >====----
  893. ;---|*|
  894. ;---|*| Add/Remove a far call from the timer registration.
  895. ;---|*|
  896. ;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission.
  897. ;---|*|
  898. ;   \*/
  899. int   pascal VESARegisterTimer( han, addr, divisor )
  900.     VESAHANDLE han;
  901.     void (far pascal *addr)();
  902.     long divisor;
  903. {
  904. int n,i,max;
  905. int retval = 0;
  906. int needsProgram = FALSE;
  907. int index;
  908. Timer_t *t;
  909.  
  910.     // Puke if we have a zero handle
  911.  
  912.         if (!han)
  913.             return(retval);
  914.  
  915.     // Make sure no interrupts come in while we're messing with the table
  916.  
  917.         _asm cli ;
  918.  
  919.     // Make sure that the special entry doesn't get used by something else
  920.  
  921.         timers[0].handle = -1;
  922.  
  923.     // If address in non-0, register it
  924.  
  925.         if (addr) {
  926.  
  927.         // See if an entry with this handle already exists
  928.  
  929.             index = findhandleindex(han);
  930.  
  931.         // If it returned -1, no entry with this handle exists, so find
  932.         // an entry with no handle
  933.  
  934.             if (index == -1)
  935.                 index = findhandleindex(0);
  936.  
  937.         // If we found an entry with a matching handle, or an empty entry,
  938.         // fill it in.
  939.  
  940.             if (index != -1) {
  941.  
  942.             // Get pointer to timer table, and cast to eliminate the volatile
  943.             // modifier (interrupts are disabled, so there's no chance of the
  944.             // structure being modified from an interrupt)
  945.  
  946.                 t = (Timer_t *)(&timers[index]);
  947.  
  948.                 t->handle = han;        // Store the handle
  949.                 t->divisor = divisor;   // Store our divisor
  950.                 t->divCount = 0;        // Clear the overflow counter
  951.                 t->callBack = addr;     // Store the callback address
  952.                 t->semaphore = 0;       // Initialize the semaphore
  953.  
  954.                 retval = -1;            // Mark as successful
  955.                 needsProgram = TRUE;    // We'll need to mess with the hardware timer
  956.             }
  957.         }
  958.         else {
  959.  
  960.         // We've got a 0 address - remove the timer entry
  961.  
  962.         // Find the entry with a matching handle
  963.  
  964.             index = findhandleindex(han);
  965.             if (index != -1) {
  966.  
  967.             // We found the entry - kill it
  968.  
  969.                 t = (Timer_t *)(&timers[index]);
  970.  
  971.                 t->handle = 0;
  972.                 t->divisor = 0;
  973.                 t->divCount = 0;
  974.                 t->callBack = 0;
  975.  
  976.                 retval = -1;            // Mark as successful
  977.                 needsProgram = TRUE;    // We'll need to mess with the hardware timer
  978.             }
  979.         }
  980.  
  981.     // If we need to, go mess with the hardware timer
  982.  
  983.         if (needsProgram)
  984.             setuptimer();
  985.  
  986.         _asm sti ;
  987.  
  988.     // Return success or failure
  989.  
  990.         return(retval);
  991. }
  992.  
  993. ;
  994. ;   /*\
  995. ;---|*|------------------=============================-------------------
  996. ;---|*|------------------====< internal routines >====-------------------
  997. ;---|*|------------------=============================-------------------
  998. ;   \*/
  999.  
  1000. ;
  1001. ;   /*\
  1002. ;---|*|----====< cmpstr >====----
  1003. ;---|*|
  1004. ;---|*| compare strings at the end of far pointers
  1005. ;---|*|
  1006. ;   \*/
  1007.  
  1008. static int cmpstr(t,s,l)
  1009.     char far *t;
  1010.     char far *s;
  1011.     int l;
  1012. {
  1013. char c;
  1014.  
  1015.     while (l--)
  1016.         if ((c = *t++ - *s++) != 0)
  1017.             return(c);
  1018.  
  1019.     return(0);
  1020. }
  1021.  
  1022. ;
  1023. ;   /*\
  1024. ;---|*|----====< FreePatchBlock >====----
  1025. ;---|*|
  1026. ;---|*| mark the block as free
  1027. ;---|*|
  1028. ;   \*/
  1029. static void pascal FreePatchBlock(void far *pptr)
  1030. {
  1031. int n;
  1032.  
  1033.     // if already full, send it back to DOS
  1034.  
  1035.         for (n=0;n<MAXFREE;n++) {
  1036.  
  1037.             // find it, and mark it free
  1038.  
  1039.             if (pptr == PatchPtrs[n]) {
  1040.                 PatchState[n] = FALSE;
  1041.                 break;
  1042.  
  1043.             }
  1044.         }
  1045. }
  1046.  
  1047. ;
  1048. ;   /*\
  1049. ;---|*|----====< GetPatchBlock >====----
  1050. ;---|*|
  1051. ;---|*| Get a block from the pool, or from DOS
  1052. ;---|*|
  1053. ;   \*/
  1054. static void far * pascal GetPatchBlock(len)
  1055.     int  len;
  1056. {
  1057. int n;
  1058. void far *vp;
  1059.  
  1060.     // scan the global pool before allocating from DOS
  1061.  
  1062.         for (n=0;n<FreeMax;n++) {
  1063.  
  1064.             if (len <= PatchLength[n])
  1065.  
  1066.                 return(PatchPtrs[n]);
  1067.  
  1068.         }
  1069.  
  1070.     // we didn't find anything, so allocate it from DOS
  1071.  
  1072.         if (FreeMax < MAXFREE) {
  1073.  
  1074.             if ((PatchPtrs[FreeMax] = vp = AllocateBuffer(len)) != 0) {
  1075.  
  1076.                 PatchLength[FreeMax] = len;
  1077.                 PatchState[FreeMax++]= TRUE;
  1078.  
  1079.             }
  1080.             return(vp);     // returns the valid pointer
  1081.         }
  1082.  
  1083.     // nothing
  1084.  
  1085.         return(FALSE);
  1086. }
  1087.  
  1088. ;
  1089. ;   /*\
  1090. ;---|*|----====< GetPatch >====----
  1091. ;---|*|
  1092. ;---|*| This function returns a far pointer to the patch block
  1093. ;---|*|
  1094. ;   \*/
  1095.  
  1096. static VAIDhdr far *pascal GetPatch ( han, patch )
  1097.     VESAHANDLE han;
  1098.     int  patch;
  1099. {
  1100. long off=-1;
  1101. int n;
  1102. VAIIhdr (far *p)[];
  1103.  
  1104.     // if an invalid patch, bomb out
  1105.  
  1106.         if ((patch > 256) || (patch < 0))
  1107.             return(FALSE);
  1108.  
  1109.         for (n=0;n<MAXLIBS;n++) {
  1110.  
  1111.             if (PatchLibs[n].han == han) {
  1112.                 off = n;
  1113.                 break;
  1114.             }
  1115.         }
  1116.  
  1117.         if (off == -1)
  1118.             return(FALSE);
  1119.  
  1120.     // if the patch is memory, return a pointer to it
  1121.  
  1122.         if (PatchLibs[n].fpVAID) {
  1123.  
  1124.             p   =  PatchLibs[n].fpVAII;
  1125.             off =  (*p)[patch].poffset - VAIIPLEN;
  1126.  
  1127.             return((VAIDhdr far *)((char far *)PatchLibs[n].fpVAID + off));
  1128.  
  1129.         }
  1130.  
  1131.     // we have to read it from disk!
  1132.  
  1133.         return(FALSE);
  1134.  
  1135. }
  1136.  
  1137. ;
  1138. ;   /*\
  1139. ;---|*|----====< GetPatchLength >====----
  1140. ;---|*|
  1141. ;---|*| Returns the length of the patches data portion (include VAIDhdr)
  1142. ;---|*|
  1143. ;   \*/
  1144. static long pascal GetPatchLength ( han, patch )
  1145.     VESAHANDLE han;
  1146.     int  patch;
  1147. {
  1148. VAIIhdr (far *p)[];
  1149. long off;
  1150. int n;
  1151.  
  1152.     // if an invalid patch, bomb out
  1153.  
  1154.         if ((patch > 256) || (patch < 0))
  1155.             return(0L);
  1156.  
  1157.     // find the patch table, then return the length of the patch data
  1158.  
  1159.         for (n=0;n<MAXLIBS;n++) {
  1160.  
  1161.             if (PatchLibs[n].han == han) {
  1162.                 p = PatchLibs[n].fpVAII;
  1163.                 return ((*p)[patch].vaidln);
  1164.             }
  1165.         }
  1166.  
  1167.     // this handle was'nt registered
  1168.  
  1169.         return(FALSE);
  1170. }
  1171.  
  1172. ;   /*\
  1173. ;---|*|----====< ourintvect >====----
  1174. ;---|*|
  1175. ;---|*| Process timer interrupts.
  1176. ;---|*|
  1177. ;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission.
  1178. ;---|*|
  1179. ;   \*/
  1180.  
  1181. #if __BORLANDC__
  1182. static interrupt void ourintvect(void)
  1183. #else
  1184. static void interrupt ourintvect(void)
  1185. #endif
  1186.  
  1187. {
  1188. int     i;
  1189. int     didEOI = FALSE;
  1190. long    timercount;
  1191. Timer_t *t;
  1192.  
  1193.     // We want to process any previously unaccounted for timer divisor
  1194.     // counts, plus the number of counts that the hardware did before
  1195.     // triggering this interrupt
  1196.  
  1197.         curtimerslop += curtimerdiv;
  1198.  
  1199.     // If we don't have too many nested timer interrupts, process the
  1200.     // individual callbacks.
  1201.  
  1202.         if (++curnestedtimer < MAXNESTEDCBS) {
  1203.  
  1204.         // Clear any accumulated slop, and use for count to advance the
  1205.         // individual callbacks' overflow values
  1206.  
  1207.             timercount = curtimerslop;
  1208.             curtimerslop = 0;
  1209.  
  1210.             t = (Timer_t *)timers;
  1211.             for (i = 0;i < CALLBACKS;i++,t++) {
  1212.  
  1213.             // If this isn't a valid entry, skip it
  1214.  
  1215.                 if (!(t->handle && t->callBack))
  1216.                     continue;
  1217.  
  1218.             // It's valid - advance its overflow value
  1219.  
  1220.                 t->divCount += timercount;
  1221.  
  1222.             // If we're past the first (special) entry, and the we didn't
  1223.             // call the old timer ISR, do the EOI now.
  1224.  
  1225.                 if (i && !didEOI) {
  1226.                     outp(0x20,0x20);
  1227.                     didEOI = TRUE;
  1228.                 }
  1229.  
  1230.             // If we're not already in a call to this one, and its overflow
  1231.             // value equals or exceeds its timer count value, set its
  1232.             // semaphore and call it.
  1233.  
  1234.                 if ((t->semaphore == 0) && (t->divCount >= t->divisor)) {
  1235.  
  1236.                     t->divCount -= t->divisor;
  1237.                     t->semaphore++;
  1238.  
  1239.                 // If it's the special entry, push the flags for the old
  1240.                 // timer ISR's IRET, and flag the EOI as having happened,
  1241.                 // as the old ISR will have issued it. If it's not the
  1242.                 // special entry, enable interrupts before the call.
  1243.  
  1244.                 if (i) {
  1245.                     _asm { sti };
  1246.                 }
  1247.                 else {
  1248.                     didEOI = TRUE;
  1249.                     _asm { pushf };
  1250.                 }
  1251.  
  1252.                 t->callBack();
  1253.  
  1254.                 // Re-disable interrupts, and flag entry as not currently
  1255.                 // being processed.
  1256.  
  1257.                     _asm { cli };
  1258.                     t->semaphore--;
  1259.  
  1260.             }
  1261.         }
  1262.     }
  1263.  
  1264.     // Decrement nested interrupt call count
  1265.  
  1266.         curnestedtimer--;
  1267.  
  1268.     // If we didn't get to the EOI before, do it now
  1269.  
  1270.         if (!didEOI)
  1271.             outp(0x20,0x20);
  1272. }
  1273.  
  1274. ;
  1275. ;   /*\
  1276. ;---|*|----====< OurFreeCBCallBack >====----
  1277. ;---|*|
  1278. ;---|*| The patch block is now free. NOTE: No assumptions can be made about
  1279. ;---|*| the segment registers! (DS,ES,GS,FS)
  1280. ;---|*|
  1281. ;   \*/
  1282.  
  1283. static void far pascal OurFreeCBCallBack( han, patch, fptr, filler )
  1284.     VESAHANDLE han; // the caller's handle
  1285.     int  patch;     // the actual patch #
  1286.     void far *fptr; // buffer that just played
  1287.     long filler;    // reserved
  1288. {
  1289.  
  1290.     // point to our data segment
  1291.  
  1292.         _asm {
  1293.             pusha
  1294.             push    ds
  1295.             push    es
  1296.             mov     ax,seg FreeMax
  1297.             mov     ds,ax
  1298.  
  1299.         }
  1300.  
  1301.     // free up the block for other patchs to use
  1302.  
  1303.         FreePatchBlock(fptr);
  1304.  
  1305.     // restore the segment & return home
  1306.  
  1307.         _asm {
  1308.             pop     es
  1309.             pop     ds
  1310.             popa
  1311.         }
  1312. }
  1313.  
  1314.  
  1315. ;
  1316. ;   /*\
  1317. ;---|*|----====< programtimer >====----
  1318. ;---|*|
  1319. ;---|*| Program the timer chip for the appropriate interrupt rate. The
  1320. ;---|*| parameter is the actual timer value, so it's 1193180/rate
  1321. ;---|*|
  1322. ;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission.
  1323. ;---|*|
  1324. ;   \*/
  1325.  
  1326. void programtimer (max)
  1327.     unsigned max;
  1328. {
  1329.         _asm    pushf
  1330.         _asm    cli
  1331.  
  1332.         _asm    mov al,0x36
  1333.         _asm    out 0x43,al
  1334.         _asm    jmp delay1
  1335.  
  1336.     delay1:;
  1337.  
  1338.         _asm    jmp delay2
  1339.  
  1340.     delay2:;
  1341.  
  1342.         _asm    mov ax,[max]
  1343.         _asm    out 0x40,al
  1344.         _asm    jmp delay3
  1345.  
  1346.     delay3:;
  1347.  
  1348.         _asm    jmp delay4
  1349.  
  1350.     delay4:;
  1351.  
  1352.         _asm    xchg ah,al
  1353.         _asm    out 0x40,al
  1354.         _asm    popf
  1355. }
  1356.  
  1357. ;
  1358. ;   /*\
  1359. ;---|*|----====< readblock >====----
  1360. ;---|*|
  1361. ;---|*| read a chunk of the PCM file into the huge buffer
  1362. ;---|*|
  1363. ;   \*/
  1364. static int readblock (han,tptr,len)
  1365.     int han;
  1366.     char huge *tptr;
  1367.     int len;
  1368. {
  1369. int siz = 0;
  1370.  
  1371.     // go get it...
  1372.  
  1373.         _asm {
  1374.             push    ds
  1375.  
  1376.             mov     cx,[len]
  1377.             mov     ax,cx
  1378.             add     cx,word ptr [tptr]  // wrap?
  1379.             jnc     rdbl05              // no, go get the size
  1380.  
  1381.             sub     ax,cx               // ax holds the # of bytes to read
  1382.             mov     cx,ax
  1383.             sub     [len],ax
  1384.  
  1385.             mov     ah,03fh             // cx holds the length
  1386.             mov     bx,[han]
  1387.             lds     dx,[tptr]
  1388.             int     21h
  1389.  
  1390.             mov     [siz],ax            // we moved this much
  1391.             add     word ptr [tptr+2],0x1000
  1392.  
  1393.             cmp     ax,cx               // same size?
  1394.             jnz     rdbldone            // no, exit...
  1395.  
  1396.         } rdbl05: _asm {
  1397.  
  1398.             mov     ah,03fh
  1399.             mov     bx,[han]
  1400.             mov     cx,[len]
  1401.  
  1402.             jcxz    rdbldone
  1403.  
  1404.             lds     dx,[tptr]
  1405.             int     21h
  1406.  
  1407.             add     [siz],ax            // we moved this much
  1408.  
  1409.         } rdbldone: _asm {
  1410.  
  1411.             pop     ds
  1412.  
  1413.         }
  1414.  
  1415.     // return the amount read
  1416.  
  1417.         return(siz);
  1418. }
  1419.  
  1420.  
  1421. ;   /*\
  1422. ;---|*|----====< setuptimer >====----
  1423. ;---|*|
  1424. ;---|*| Something has changed the timer structures, so we need to figure
  1425. ;---|*| out what to do with the hardware timer and the hardware timer ISR
  1426. ;---|*|
  1427. ;---|*| This is only called by VESARegisterTimer(), so we assume that
  1428. ;---|*| interrupts are disabled.
  1429. ;---|*|
  1430. ;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission.
  1431. ;---|*|
  1432. ;   \*/
  1433. void
  1434. setuptimer(void)
  1435. {
  1436. int     i,j;
  1437. int     otherCount;
  1438. long    minDiv,minDivDelta,timerVal;
  1439. Timer_t *t,*t2;
  1440.  
  1441.     // If we haven't filled out the special slot 0 entry, do so now
  1442.  
  1443.         if (!timers[0].callBack) {
  1444.             timers[0].callBack = (void(far pascal *)(void))_dos_getvect(8);
  1445.             timers[0].divisor  = 0x10000L;
  1446.             timers[0].divCount = 0;
  1447.         }
  1448.  
  1449.     // Count the number of non-special entries, and find 1) The entry
  1450.     // with the smallest timer divisor, and 2) The smallest difference
  1451.     // between divisors.
  1452.  
  1453.         minDiv = 0x10000L;
  1454.         minDivDelta = 0x10000L;
  1455.         otherCount = 0;
  1456.         t = (Timer_t *)(&timers[1]);
  1457.         for (i = 1;i < CALLBACKS;i++,t++) {
  1458.  
  1459.         // Only pay attention to valid entries
  1460.  
  1461.             if (t->handle) {
  1462.  
  1463.             // Count this non-special entry
  1464.  
  1465.                 otherCount++;
  1466.  
  1467.             // If this entry's divisor is smaller than our previous minimum,
  1468.             // use it for our new minimum.
  1469.  
  1470.                 if (t->divisor && (t->divisor < minDiv))
  1471.                     minDiv = t->divisor;
  1472.  
  1473.             // Go through the table again, finding the smallest non-zero
  1474.             // difference between two divisors
  1475.  
  1476.                 t2 = (Timer_t *)(&timers[0]);
  1477.                 for (j = 0;j < CALLBACKS;j++,t2++) {
  1478.  
  1479.                     if ((j != i) && (t2->handle)) {
  1480.  
  1481.                         long    delta;
  1482.  
  1483.                     // Get absolute value of difference between the two
  1484.                     // entries' divisors
  1485.  
  1486.                         delta = t2->divisor - t->divisor;
  1487.  
  1488.                         if (delta < 0)
  1489.                             delta = -delta;
  1490.  
  1491.                     // If non-zero, and smaller than previous smallest
  1492.                     // difference, hold on to it
  1493.  
  1494.                         if (delta && (delta < minDivDelta))
  1495.                             minDivDelta = delta;
  1496.                     }
  1497.                 }
  1498.             }
  1499.         }
  1500.  
  1501.     // If we no longer need to own the vector, and our ISR is installed,
  1502.     // remove it
  1503.  
  1504.         if ((otherCount == 0) && timerhooked) {
  1505.  
  1506. #if __BORLANDC__ //DSC
  1507.         _dos_setvect(8,(void interrupt (*)())(timers[0].callBack));
  1508. #else
  1509.         _dos_setvect(8,(void (interrupt far *)())(timers[0].callBack));
  1510. #endif
  1511.         timerhooked = FALSE;
  1512.  
  1513.         }
  1514.  
  1515.     // If we need to own the vector, and our ISR isn't installed,
  1516.     // install it
  1517.  
  1518.         if (otherCount && !timerhooked) {
  1519.             _dos_setvect(8,ourintvect);
  1520.             timerhooked = TRUE;
  1521.         }
  1522.  
  1523.     // Set timer divisor to minimum divisor
  1524.  
  1525.         timerVal = minDiv;
  1526.  
  1527.     //  This next bit is here to help out in situations where there are a
  1528.     // couple of fairly slow callbacks set up, and no fast callbacks. If the
  1529.     // only callbacks set up are at 40Hz and 50Hz, and this code weren't
  1530.     // here, the hardware would be set up to generate 50Hz interrupts.
  1531.     // Starting at a time of 0.0, the interrupts would be called at:
  1532.     // Time     40Hz    50Hz
  1533.     // ----     ----    ----
  1534.     // 0.02             x
  1535.     // 0.04     x       x
  1536.     // 0.06     x       x
  1537.     // 0.08     x       x
  1538.     // 0.10     x       x
  1539.     // 0.12             x
  1540.     // 0.14     x       x
  1541.     //
  1542.     // etc. In other words, four out of every five of the 40Hz callbacks
  1543.     // would be performed with 0.02 seconds in between, and then there
  1544.     // would be a 0.04 second delay before the next call to the 40Hz
  1545.     // callback. Although the 40Hz callback will get called 40 times a
  1546.     // second, the time between calls won't be regular.
  1547.     //  Although this isn't a problem under some circumstances, it can be a
  1548.     // problem under other circumstances, and a nuisance under still other
  1549.     // circumstances.
  1550.     //  This code can increase the timer resolution to about 145.6Hz,
  1551.     // which translates to about 6.87ms (0.00687 seconds). This means that
  1552.     // a particular callback will occur, at most, roughly 3.435ms before
  1553.     // or after it "should".
  1554.     //  Note that by changing the value 0x2000, you can alter the adjustment
  1555.     // frequency that will be used.
  1556.     //
  1557.  
  1558.         if (timerVal > minDivDelta) {
  1559.  
  1560.             if (minDivDelta >= 0x2000)
  1561.                 timerVal = minDivDelta;
  1562.             else if (timerVal > 0x2000)
  1563.                 timerVal = 0x2000;
  1564.         }
  1565.  
  1566.     // Set the hardware timer value. It's possible that timerVal will be
  1567.     // 0x10000L, more than will fit into a 16-bit word. This is ok, as
  1568.     // programming the timer for 0x0000 is treated by the hardware as
  1569.     // programming the timer for 0x10000.
  1570.  
  1571.         programtimer((unsigned)timerVal);
  1572.         curtimerdiv = timerVal;
  1573. }
  1574.  
  1575. ;
  1576. ;   /*\
  1577. ;---|*|----====< findhandleindex >====----
  1578. ;---|*|
  1579. ;---|*| This finds an entry in the timers[] array with a handle value
  1580. ;---|*| that matches the parameter. Returns -1 if a matching entry
  1581. ;---|*| isn't found.
  1582. ;---|*|
  1583. ;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission.
  1584. ;---|*|
  1585. ;   \*/
  1586. static int findhandleindex(VESAHANDLE han)
  1587. {
  1588.     int i;
  1589.  
  1590.     for (i = 0;i < CALLBACKS;i++)
  1591.         if (timers[i].handle == han)
  1592.             return(i);
  1593.     return(-1);
  1594. }
  1595.  
  1596. ;   /*\
  1597. ;---|*| end of VESA.C
  1598. ;   \*/
  1599.  
  1600.  
  1601.  
  1602.  
  1603.