www.pudn.com > vmix_1.013.zip > VESA.C


 
;   /*\ 
;---|*|----====< VESA >====---- 
;---|*| 
;---|*| high level support routines for the VESA AI interface 
;---|*| 
;---|*| Copyright (c) 1993,1994  V.E.S.A, Inc. All Rights Reserved. 
;---|*| 
;---|*| Portions Copyright (c) 1993 Jason Blochowiak, Argo Games. Used 
;---|*| with permission. 
;---|*| 
;---|*| VBE/AI 1.0 Specification 
;---|*|    February 2, 1994. 1.00 release 
;---|*| 
;   \*/ 
 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
 
#include "vbeai.h" 
#include "vesa.h" 
 
#define TRUE    -1 
#define FALSE    0 
#define ON      TRUE 
#define OFF     FALSE 
 
#if DEBUG 
#define BREAKPOINT _asm{int 3}; 
#else 
#define BREAKPOINT _asm{nop}; 
#endif 
 
;   /*\ 
;---|*| Global Variables 
;   \*/ 
 
        // nada... 
 
;   /*\ 
;---|*| Local Variables 
;   \*/ 
 
#define CALLBACKS       6  // Maximum number of callbacks that can be installed 
#define MAXNESTEDCBS    3   // Maximum nesting of timer interrupts 
 
        typedef struct { 
            VESAHANDLE  handle;                         // The device handle 
            void        (far pascal *callBack)(void);   // The callback function 
            short       semaphore;                      // Our lockout semaphore for this callback 
            long        divisor;                        // The timer divisor this callback wants 
            long        divCount;                       // Overflow counter for this callback 
        } Timer_t; 
 
    // timer callback control table 
 
        static volatile Timer_t timers[CALLBACKS]; 
 
    // a table to store data from device info blocks 
 
        static int  doreg[CALLBACKS][2]    =      // do give ticks to this dev 
            {{0,0},{0,0},{0,0},{0,0}, 
			 {0,0},{0,0}}; 
			 /*,{0,0},{0,0}, 
			 {0,0},{0,0},{0,0},{0,0}, 
			 {0,0},{0,0},{0,0},{0,0}};*/ 
 
        static int  denom      = 18;            // decimation denominator 
        static int  isopen     = 0;             // number of devices opened 
 
        static int  timerhooked = FALSE;        // TRUE if timer is currently hooked 
        static int  curnestedtimer = 0;         // Number of nested timer interrupts 
        static long curtimerdiv = 0x10000L;     // Current hardware timer divisor 
        static long curtimerslop = 0;           // Number of timer counts that we've missed, due to excessive nesting 
 
    // general prototypes 
 
        static int  cmpstr                      ( char far *, char far *, int ); 
 
    // timer prototypes 
 
        static int  findhandleindex ( VESAHANDLE han ); 
        static void setuptimer      ( void ); 
        static void programtimer    ( unsigned ); 
        static void interrupt ourintvect ( void ); 
 
    // patch handling prototypes 
 
        static void pascal FreePatchBlock       ( void far * ); 
        static void far * pascal GetPatchBlock  ( int ); 
        static VAIDhdr far *pascal GetPatch     ( VESAHANDLE, int ); 
        static long pascal GetPatchLength       ( VESAHANDLE, int ); 
        static void far pascal OurFreeCBCallBack( VESAHANDLE, int, void far *, long ); 
 
    // patch handler data structures 
 
        typedef struct { 
            int  han;                   // VBE/AI handle 
            fpMIDServ ms;               // far pointer to the services structure 
            long    poffset;            // offset into the file to VAIP 
            FILE   *fp;                 // pointer to the patch file 
            VAIIhdr (far *fpVAII)[];    // far pointer to the index portion 
            VAIDhdr (far *fpVAID)[];    // far pointer to the data portion, if loaded 
        } PatchCTL; 
 
#define MAXLIBS     4 
#define VAIIPLEN    (sizeof(VAIIhdr)*256) 
        static PatchCTL PatchLibs[MAXLIBS] = // static of libraries can be 
            { 0,0,0,0,0,0, 
              0,0,0,0,0,0, 
              0,0,0,0,0,0, 
              0,0,0,0,0,0 
            }; 
 
    // the table of Patch blocks for use by the MIDI drivers. This is a 
    // global pool for all MIDI drivers. 
 
#define MAXFREE     64                      // maximum free blocks we save 
        static int       FreeMax = 0;       // last free position 
        static void far *PatchPtrs[MAXFREE]; 
        static long      PatchLength[MAXFREE]; 
        static int       PatchState[MAXFREE] =  // TRUE=inuse, FALSE=available 
            { 0 }; 
 
; 
;   /*\ 
;---|*|------------------==============================------------------- 
;---|*|------------------====< Start of execution >====------------------- 
;---|*|------------------==============================------------------- 
;   \*/ 
 
; 
;   /*\ 
;---|*|----====< AllocateBuffer >====---- 
;---|*| 
;---|*| Allocate some huge memory from the DOS pool 
;---|*| 
;   \*/ 
void far * pascal AllocateBuffer(siz) 
    long siz; 
{ 
void far *f; 
 
    // go to DOS to get the memory block size 
 
        _asm { 
 
        // get the # of paragraphs into BX 
 
            mov     bx,word ptr [siz+0] // 0xLLLL 
            mov     dx,word ptr [siz+2] // 0x000H 
            shr     bx,4            // 0x0LLL 
            shl     dx,12           // 0xH000 
            add     bx,dx           // 0xHLLL: bx = 16 bits out of 20 bits 
            inc     bx              // get one more 16 byte block 
            inc     bx              // get one more 16 byte block 
 
        // call DOS for a memory block 
 
            mov ah,48h 
            int 21h 
 
        // carry is set if in error. 
 
            cmc                     // set carry if no error 
            sbb     bx,bx 
            and     ax,bx           // ax = 0 if in error, else a segment 
            sub     dx,dx 
            xchg    ax,dx           // dx:ax holds the pointer 
 
            mov     word ptr [f+0],ax 
            mov     word ptr [f+2],dx 
        } 
 
    // return the modified, or empty pointer 
 
        return(f); 
} 
 
; 
;   /*\ 
;---|*|----====< FreeBuffer >====---- 
;---|*| 
;---|*| Allocate some huge memory from the DOS pool 
;---|*| 
;   \*/ 
void pascal FreeBuffer(void far *p) 
{ 
void far *f; 
 
    // go to DOS to release the memory block size 
 
        _asm { 
 
        // call DOS for a memory block 
 
            mov es,word ptr [p+2] 
            mov ah,49h 
            int 21h 
 
        } 
} 
 
; 
;   /*\ 
;---|*|----====< VESAFindADevice >====---- 
;---|*| 
;---|*| This function returns the next available device handle 
;---|*| from the driver list. 
;---|*| 
;   \*/ 
 
VESAHANDLE pascal VESAFindADevice ( class ) 
    int class; 
{ 
static VESAHANDLE lh = 0; 
 
    // if this is a reset call, do it... 
 
        if (class == -1) 
            return(lh = 0); // start over... 
 
    // call the driver directly 
 
        _asm { 
            mov     ax,VESAFUNCID 
            mov     bx,VESAFUNC1 
            mov     cx,[lh] 
            mov     dx,[class] 
            int     INTHOOK 
            mov     [lh],cx 
        } 
 
    // return the finding 
 
        return (lh); 
 
} 
 
; 
;   /*\ 
;---|*|----====< VESAQueryDevice >====---- 
;---|*| 
;---|*| Query the driver for general or device specific data. 
;---|*| 
;   \*/ 
long pascal VESAQueryDevice ( han, query, ptr ) 
    VESAHANDLE han; 
    int        query; 
    void far  *ptr; 
{ 
long cc; 
int ticks,n; 
 
    // ask the driver for some specific information 
 
        cc = (long) ptr; // we have to do this due to MS bugs in inline code! 
 
        _asm { 
            push    si 
            push    di 
 
            mov     ax,VESAFUNCID 
            mov     bx,VESAFUNC2 
            mov     cx,[han] 
            mov     dx,[query] 
 
            mov     si,word ptr [cc+2] 
            mov     di,word ptr [cc+0] 
 
            int     INTHOOK 
 
            sub     ax,004Fh        // considered a good return 
            neg     ax              // null out SI:DI if AX != 0x004F 
            sbb     ax,ax 
            not     ax 
            and     si,ax 
            and     di,ax 
 
            mov     word ptr [cc+2],si 
            mov     word ptr [cc+0],di 
 
            pop     di 
            pop     si 
        } 
 
    // if just a length query, return the value now 
 
        if (query == VESAQUERY1) 
            return (cc); 
        if (query == VESAQUERY3) 
            return (cc); 
        if (query == VESAQUERY5) 
            return (cc); 
 
    // if this is returning the info block, grab the timer tick count 
    // since this is the only time we may see the INFO block. 
 
        if (query == VESAQUERY2) { 
 
            switch (((fpGDC)cc)->gdclassid) { 
 
                case WAVDEVICE: 
                    ticks = ((fpGDC)cc)->u.gdwi.witimerticks; 
                    break; 
 
                case MIDDEVICE: 
                    ticks = ((fpGDC)cc)->u.gdmi.mitimerticks; 
                    break; 
 
                default: 
                case VOLDEVICE: 
                    ticks = 0;  // no timer ticks for these types 
                    break; 
            } 
 
            // see if the handle is already registered, if so, we're done 
 
            for (n=1;n====---- 
;---|*| 
;---|*| This function returns a pointer to the driver 
;---|*| services table if the driver opens successfully. 
;---|*| 
;   \*/ 
void far *pascal VESAOpenADevice ( han, apiset, memptr ) 
    VESAHANDLE han; 
    int apiset; 
    void far *memptr; 
{ 
void far *cc = 0; 
int n; 
 
    // attempt to open the device 
 
        _asm { 
            cmp     word ptr [memptr+0],0   // offset must be null 
            jnz     voad_05 
 
            mov     ax,VESAFUNCID           // open function 
            mov     bx,VESAFUNC3 
            mov     cx,[han]                // device handle is required 
            mov     dx,[apiset]             // select 16/32 bit interface 
            mov     si,word ptr [memptr+2]  // pass in the segment 
            int     INTHOOK                 // do the interrupt 
 
            mov     word ptr [cc+2],si 
            mov     word ptr [cc+0],cx 
        } 
        voad_05:; 
 
    // if successful, check to see if the device needs timer ticks 
 
        if (cc) { 
 
            for (n=1;nwsname,"WAVS",4) == 0 ) 
 
                        VESARegisterTimer 
                          ( 
                            han,                            // handle 
                            ((fpWAVServ)cc)->wsTimerTick,   // address 
                            VESARateToDivisor(doreg[n][1])  // tick count 
                          ); 
 
                    // register midi audio 
 
                    if ( _fstrncmp(((fpMIDServ)cc)->msname,"MIDS",4) == 0 ) 
 
                        VESARegisterTimer 
                          ( 
                            han,                            // handle 
                            ((fpMIDServ)cc)->msTimerTick,   // address 
                            VESARateToDivisor(doreg[n][1])  // tick count 
                          ); 
 
                    break; 
                } 
            } 
        } 
 
    // return the pointer to the services table 
 
        return(cc); 
 
} 
 
; 
;   /*\ 
;---|*|----====< VESACloseDevice >====---- 
;---|*| 
;---|*| This function closes an opened device. 
;---|*| 
;   \*/ 
void  pascal VESACloseDevice ( han ) 
    VESAHANDLE han; 
{ 
int n,gd; 
 
    // remove the timer callbacks for this handle 
 
        VESARegisterTimer( han, 0, 0 ); 
 
    // make sure MIDI devices free up bank memory 
 
        VESAFreePatchBank ( han ); 
 
    // go close the device. 
 
        _asm { 
            mov     ax,VESAFUNCID 
            mov     bx,VESAFUNC4 
            mov     cx,[han]            // if the handle exists, the 
            int     INTHOOK             // device will close 
        } 
} 
 
; 
;   /*\ 
;---|*|----====< VESAFreePatchBank >====---- 
;---|*| 
;---|*| This function will release the patch bank data memory and 
;---|*| remove the VBE device from the internal tables. 
;---|*| 
;   \*/ 
int  pascal VESAFreePatchBank ( han ) 
    VESAHANDLE han; // device handle 
{ 
int n; 
int inuse = 0; 
 
    for (n=0;n====---- 
;---|*| 
;---|*| This function will load the patch bank index structure into memory. 
;---|*| If the data portion is small, then it too will be loaded. 
;---|*| 
;   \*/ 
int  pascal VESALoadPatchBank ( han, ms, bnk ) 
    VESAHANDLE han; // device handle 
    fpMIDServ ms;   // fp to services structure 
    char far *bnk; 
{ 
FILE *fp; 
long len,l; 
int  n,idx=-1; 
char far *pt,far *opt, far *patchbank; 
char fname[100],*s,*b; 
char *env,c; 
 
    // make sure we don't already have this device's bank or are already 
    // filled with patch banks 
 
        for (c=n=0;ntype[0], "RIFF",4) != 0) { 
            fclose (fp); 
            return(FALSE); 
        } 
        pt += sizeof(RIFFhdr);          // skip the header 
 
    // load in the first chunk. The first "vail" block must be here! 
 
        if (cmpstr(&((VAILhdr far *)pt)->type[0], "vail",4) != 0) { 
            fclose (fp); 
            return(FALSE); 
        } 
        pt += sizeof(VAILhdr);          // skip the header 
 
    // loop through till the vaii header is located 
 
        while (1) { 
 
            opt = pt; 
 
            if (cmpstr(&((ZSTRhdr far *)pt)->type[0], "ZSTR",4) == 0) { 
                l = ((ZSTRhdr far *)pt)->tlen; 
                pt += sizeof(ZSTRhdr)+l; // skip the ASCIIZ block 
            } 
 
            if (cmpstr(&((VAIPhdr far *)pt)->type[0], "vaip",4) == 0) { 
                pt += sizeof (VAIPhdr); // move past this block 
                break;                  // we're done, save this pointer! 
            } 
 
            if (opt == pt) { 
                fclose (fp); 
                return(FALSE);          // invalid file format 
            } 
        } 
 
    // we have the start of the VAII record. reposition the file pointer, 
    // then pull in the entire index. 
 
        len = (int)pt - (int)patchbank;     // get the starting offset 
        fseek (fp,len,SEEK_SET); 
 
    // save the starting position of the VAIP record 
 
        PatchLibs[idx].poffset= ftell(fp) - sizeof (VAIPhdr); 
 
        if (readblock (fileno(fp),patchbank,VAIIPLEN & 0xffff) != VAIIPLEN) { 
            fclose (fp); 
            return(FALSE); 
        } 
 
    // register this VESA device in our system 
 
        PatchLibs[idx].han    = han;    // record this new entry 
        PatchLibs[idx].ms     = ms; 
        (void far *)PatchLibs[idx].fpVAII = (void far *)patchbank; 
 
    // grab the callback since we're doing the patch handling 
 
        ms->msApplFreeCB = &OurFreeCBCallBack; 
 
    // get the unused file length to determine if we can preload the patch data 
 
        len  = filelength (fileno(fp)); // patch size = file len. - index 
        len -= ftell(fp); 
 
    // just load banks less than 64K 
 
        if (len < 65535) { 
 
            // if not enough memory, skip it... 
 
                if ((patchbank = NewBuf(len)) == 0) 
                    return(TRUE); 
 
            // read in the patch data now. If not, we'll do the slow method 
 
                if (((long)readblock(fileno(fp),patchbank,(int)len) & 0xffff) != len) 
                    return(TRUE); 
 
            // record the patch data buffer now 
 
                (void far *)PatchLibs[idx].fpVAID = (void far *)patchbank; 
                fclose (fp); 
        } 
 
    // if >64K, then we need to go to disk for each patch. 
 
        else { 
 
            // save the file pointer for later access when we need the data 
 
            PatchLibs[n].fp = fp; 
        } 
 
    // return the length of the structure 
 
        return(TRUE); 
 
} 
 
; 
;   /*\ 
;---|*|----====< VESAPreloadPatch >====---- 
;---|*| 
;---|*| Download the VESA patch to the driver. 
;---|*| 
;---|*| NOTE: This routine may call DOS, so use it only in the foreground! 
;---|*| 
;   \*/ 
int  pascal VESAPreLoadPatch ( han, patch, ch ) 
    VESAHANDLE han; // device handle 
    int patch;      // patch # to be downloaded 
    int ch;         // channel # for download 
{ 
long isdata; 
int n,trim; 
char far *pptr; 
 
    // percussive patches reside in the second half of the table 
 
        if (ch == 9) 
            patch |= 0x80; 
 
    // get the patch and size of patch 
 
        for (n=0;nmsPreLoadPatch) 
                          ( 
                            ch, 
                            patch & 0x7F, 
                            GetPatch       ( han, patch ), // sizeof(VAIDhdr), 
                            GetPatchLength ( han, patch ) 
                          ); 
 
                        break;  // exit the for loop 
                } 
 
                else { 
 
                    // get the length needed 
 
                        isdata = GetPatchLength ( han, patch ); 
                        trim   = sizeof (VAIDhdr); 
 
                        while (isdata) { 
 
                            // find the size needed 
 
                            n = (*PatchLibs[n].ms->msPreLoadPatch)(ch,patch&0x7F,0,0); 
 
                            // if we get a block, process the data 
 
                            if (pptr = GetPatchBlock(n)) { 
 
                                // read it from disk 
 
                                fseek 
                                  ( 
                                    PatchLibs[n].fp, 
                                    PatchLibs[n].poffset+ 
                                        (*PatchLibs[n].fpVAII)[patch].poffset, 
                                    SEEK_SET 
                                  ); 
 
                                n = readblock (fileno(PatchLibs[n].fp),pptr,n); 
 
                                // send it to the driver, and maybe free it up 
 
                                if (!(*PatchLibs[n].ms->msPreLoadPatch) 
                                  ( 
                                    ch, 
                                    patch&0x7F, 
                                    pptr+trim, 
                                    n 
                                  )) 
                                    FreePatchBlock(pptr); 
 
                                // remove our portion 
 
                                trim    = 0; 
                                isdata -= n; 
                            } 
 
                            // oops, no data, let's pull back... 
 
                            else { 
                                (*PatchLibs[n].ms->msUnloadPatch) (ch,patch&0x7F); 
                                break; 
                            } 
 
                        } 
 
                    break;  // exit the for loop 
                } 
            } 
        } 
} 
 
; 
;   /*\ 
;---|*|----====< VESARateToDivisor >====---- 
;---|*| 
;---|*| Converts an integral rate (in Hz) to a timer divisor value, which 
;---|*| can then be passed to VESARegisterTimer(). 
;---|*| 
;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission. 
;---|*| 
;   \*/ 
long pascal VESARateToDivisor(rate) 
    long rate; 
{ 
long result; 
 
    if (rate) 
        result = 1193180L / rate; 
    else 
        result = 0x10000L; 
 
    return(result); 
} 
 
; 
;   /*\ 
;---|*|----====< VESARegisterTimer >====---- 
;---|*| 
;---|*| Add/Remove a far call from the timer registration. 
;---|*| 
;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission. 
;---|*| 
;   \*/ 
int   pascal VESARegisterTimer( han, addr, divisor ) 
    VESAHANDLE han; 
    void (far pascal *addr)(); 
    long divisor; 
{ 
int n,i,max; 
int retval = 0; 
int needsProgram = FALSE; 
int index; 
Timer_t *t; 
 
    // Puke if we have a zero handle 
 
        if (!han) 
            return(retval); 
 
    // Make sure no interrupts come in while we're messing with the table 
 
        _asm cli ; 
 
    // Make sure that the special entry doesn't get used by something else 
 
        timers[0].handle = -1; 
 
    // If address in non-0, register it 
 
        if (addr) { 
 
        // See if an entry with this handle already exists 
 
            index = findhandleindex(han); 
 
        // If it returned -1, no entry with this handle exists, so find 
        // an entry with no handle 
 
            if (index == -1) 
                index = findhandleindex(0); 
 
        // If we found an entry with a matching handle, or an empty entry, 
        // fill it in. 
 
            if (index != -1) { 
 
            // Get pointer to timer table, and cast to eliminate the volatile 
            // modifier (interrupts are disabled, so there's no chance of the 
            // structure being modified from an interrupt) 
 
                t = (Timer_t *)(&timers[index]); 
 
                t->handle = han;        // Store the handle 
                t->divisor = divisor;   // Store our divisor 
                t->divCount = 0;        // Clear the overflow counter 
                t->callBack = addr;     // Store the callback address 
                t->semaphore = 0;       // Initialize the semaphore 
 
                retval = -1;            // Mark as successful 
                needsProgram = TRUE;    // We'll need to mess with the hardware timer 
            } 
        } 
        else { 
 
        // We've got a 0 address - remove the timer entry 
 
        // Find the entry with a matching handle 
 
            index = findhandleindex(han); 
            if (index != -1) { 
 
            // We found the entry - kill it 
 
                t = (Timer_t *)(&timers[index]); 
 
                t->handle = 0; 
                t->divisor = 0; 
                t->divCount = 0; 
                t->callBack = 0; 
 
                retval = -1;            // Mark as successful 
                needsProgram = TRUE;    // We'll need to mess with the hardware timer 
            } 
        } 
 
    // If we need to, go mess with the hardware timer 
 
        if (needsProgram) 
            setuptimer(); 
 
        _asm sti ; 
 
    // Return success or failure 
 
        return(retval); 
} 
 
; 
;   /*\ 
;---|*|----====< readblock >====---- 
;---|*| 
;---|*| read a chunk of the PCM file into the huge buffer 
;---|*| 
;   \*/ 
int readblock (han,tptr,len) 
    int han; 
    char huge *tptr; 
    int len; 
{ 
int siz = 0; 
 
    // go get it... 
 
        _asm { 
            push    ds 
 
            mov     cx,[len] 
            mov     ax,cx 
            add     cx,word ptr [tptr]  // wrap? 
            jnc     rdbl05              // no, go get the size 
 
            sub     ax,cx               // ax holds the # of bytes to read 
            mov     cx,ax 
            sub     [len],ax 
 
            mov     ah,03fh             // cx holds the length 
            mov     bx,[han] 
            lds     dx,[tptr] 
            int     21h 
 
            mov     [siz],ax            // we moved this much 
            add     word ptr [tptr+2],0x1000 
 
            cmp     ax,cx               // same size? 
            jnz     rdbldone            // no, exit... 
 
        } rdbl05: _asm { 
 
            mov     ah,03fh 
            mov     bx,[han] 
            mov     cx,[len] 
 
            jcxz    rdbldone 
 
            lds     dx,[tptr] 
            int     21h 
 
            add     [siz],ax            // we moved this much 
 
        } rdbldone: _asm { 
 
            pop     ds 
 
        } 
 
    // return the amount read 
 
        return(siz); 
} 
 
 
 
; 
;   /*\ 
;---|*|------------------=============================------------------- 
;---|*|------------------====< internal routines >====------------------- 
;---|*|------------------=============================------------------- 
;   \*/ 
 
; 
;   /*\ 
;---|*|----====< cmpstr >====---- 
;---|*| 
;---|*| compare strings at the end of far pointers 
;---|*| 
;   \*/ 
 
static int cmpstr(t,s,l) 
    char far *t; 
    char far *s; 
    int l; 
{ 
char c; 
 
    while (l--) 
        if ((c = *t++ - *s++) != 0) 
            return(c); 
 
    return(0); 
} 
 
; 
;   /*\ 
;---|*|----====< FreePatchBlock >====---- 
;---|*| 
;---|*| mark the block as free 
;---|*| 
;   \*/ 
static void pascal FreePatchBlock(void far *pptr) 
{ 
int n; 
 
    // if already full, send it back to DOS 
 
        for (n=0;n====---- 
;---|*| 
;---|*| Get a block from the pool, or from DOS 
;---|*| 
;   \*/ 
static void far * pascal GetPatchBlock(len) 
    int  len; 
{ 
int n; 
void far *vp; 
 
    // scan the global pool before allocating from DOS 
 
        for (n=0;n====---- 
;---|*| 
;---|*| This function returns a far pointer to the patch block 
;---|*| 
;   \*/ 
 
static VAIDhdr far *pascal GetPatch ( han, patch ) 
    VESAHANDLE han; 
    int  patch; 
{ 
long off=-1; 
int n; 
VAIIhdr (far *p)[]; 
 
    // if an invalid patch, bomb out 
 
        if ((patch > 256) || (patch < 0)) 
            return(FALSE); 
 
        for (n=0;n====---- 
;---|*| 
;---|*| Returns the length of the patches data portion (include VAIDhdr) 
;---|*| 
;   \*/ 
static long pascal GetPatchLength ( han, patch ) 
    VESAHANDLE han; 
    int  patch; 
{ 
VAIIhdr (far *p)[]; 
long off; 
int n; 
 
    // if an invalid patch, bomb out 
 
        if ((patch > 256) || (patch < 0)) 
            return(0L); 
 
    // find the patch table, then return the length of the patch data 
 
        for (n=0;n====---- 
;---|*| 
;---|*| Process timer interrupts. 
;---|*| 
;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission. 
;---|*| 
;   \*/ 
 
#if __BORLANDC__ 
static interrupt void ourintvect(void) 
#else 
static void interrupt ourintvect(void) 
#endif 
 
{ 
int     i; 
int     didEOI = FALSE; 
long    timercount; 
Timer_t *t; 
 
    // We want to process any previously unaccounted for timer divisor 
    // counts, plus the number of counts that the hardware did before 
    // triggering this interrupt 
 
        curtimerslop += curtimerdiv; 
 
    // If we don't have too many nested timer interrupts, process the 
    // individual callbacks. 
 
        if (++curnestedtimer < MAXNESTEDCBS) { 
 
        // Clear any accumulated slop, and use for count to advance the 
        // individual callbacks' overflow values 
 
            timercount = curtimerslop; 
            curtimerslop = 0; 
 
            t = (Timer_t *)timers; 
            for (i = 0;i < CALLBACKS;i++,t++) { 
 
            // If this isn't a valid entry, skip it 
 
                if (!(t->handle && t->callBack)) 
                    continue; 
 
            // It's valid - advance its overflow value 
 
                t->divCount += timercount; 
 
            // If we're past the first (special) entry, and the we didn't 
            // call the old timer ISR, do the EOI now. 
 
                if (i && !didEOI) { 
                    outp(0x20,0x20); 
                    didEOI = TRUE; 
                } 
 
            // If we're not already in a call to this one, and its overflow 
            // value equals or exceeds its timer count value, set its 
            // semaphore and call it. 
 
                if ((t->semaphore == 0) && (t->divCount >= t->divisor)) { 
 
                    t->divCount -= t->divisor; 
                    t->semaphore++; 
 
                // If it's the special entry, push the flags for the old 
                // timer ISR's IRET, and flag the EOI as having happened, 
                // as the old ISR will have issued it. If it's not the 
                // special entry, enable interrupts before the call. 
 
                if (i) { 
                    _asm { sti }; 
                } 
                else { 
                    didEOI = TRUE; 
                    _asm { pushf }; 
                } 
 
                t->callBack(); 
 
                // Re-disable interrupts, and flag entry as not currently 
                // being processed. 
 
                    _asm { cli }; 
                    t->semaphore--; 
 
            } 
        } 
    } 
 
    // Decrement nested interrupt call count 
 
        curnestedtimer--; 
 
    // If we didn't get to the EOI before, do it now 
 
        if (!didEOI) 
            outp(0x20,0x20); 
} 
 
; 
;   /*\ 
;---|*|----====< OurFreeCBCallBack >====---- 
;---|*| 
;---|*| The patch block is now free. NOTE: No assumptions can be made about 
;---|*| the segment registers! (DS,ES,GS,FS) 
;---|*| 
;   \*/ 
 
static void far pascal OurFreeCBCallBack( han, patch, fptr, filler ) 
    VESAHANDLE han; // the caller's handle 
    int  patch;     // the actual patch # 
    void far *fptr; // buffer that just played 
    long filler;    // reserved 
{ 
 
    // point to our data segment 
 
        _asm { 
            pusha 
            push    ds 
            push    es 
            mov     ax,seg FreeMax 
            mov     ds,ax 
 
        } 
 
    // free up the block for other patchs to use 
 
        FreePatchBlock(fptr); 
 
    // restore the segment & return home 
 
        _asm { 
            pop     es 
            pop     ds 
            popa 
        } 
} 
 
 
; 
;   /*\ 
;---|*|----====< programtimer >====---- 
;---|*| 
;---|*| Program the timer chip for the appropriate interrupt rate. The 
;---|*| parameter is the actual timer value, so it's 1193180/rate 
;---|*| 
;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission. 
;---|*| 
;   \*/ 
 
void programtimer (max) 
    unsigned max; 
{ 
        _asm    pushf 
        _asm    cli 
 
        _asm    mov al,0x36 
        _asm    out 0x43,al 
        _asm    jmp delay1 
 
    delay1:; 
 
        _asm    jmp delay2 
 
    delay2:; 
 
        _asm    mov ax,[max] 
        _asm    out 0x40,al 
        _asm    jmp delay3 
 
    delay3:; 
 
        _asm    jmp delay4 
 
    delay4:; 
 
        _asm    xchg ah,al 
        _asm    out 0x40,al 
        _asm    popf 
} 
 
 
;   /*\ 
;---|*|----====< setuptimer >====---- 
;---|*| 
;---|*| Something has changed the timer structures, so we need to figure 
;---|*| out what to do with the hardware timer and the hardware timer ISR 
;---|*| 
;---|*| This is only called by VESARegisterTimer(), so we assume that 
;---|*| interrupts are disabled. 
;---|*| 
;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission. 
;---|*| 
;   \*/ 
void 
setuptimer(void) 
{ 
int     i,j; 
int     otherCount; 
long    minDiv,minDivDelta,timerVal; 
Timer_t *t,*t2; 
 
    // If we haven't filled out the special slot 0 entry, do so now 
 
        if (!timers[0].callBack) { 
            timers[0].callBack = (void(far pascal *)(void))_dos_getvect(8); 
            timers[0].divisor  = 0x10000L; 
            timers[0].divCount = 0; 
        } 
 
    // Count the number of non-special entries, and find 1) The entry 
    // with the smallest timer divisor, and 2) The smallest difference 
    // between divisors. 
 
        minDiv = 0x10000L; 
        minDivDelta = 0x10000L; 
        otherCount = 0; 
        t = (Timer_t *)(&timers[1]); 
        for (i = 1;i < CALLBACKS;i++,t++) { 
 
        // Only pay attention to valid entries 
 
            if (t->handle) { 
 
            // Count this non-special entry 
 
                otherCount++; 
 
            // If this entry's divisor is smaller than our previous minimum, 
            // use it for our new minimum. 
 
                if (t->divisor && (t->divisor < minDiv)) 
                    minDiv = t->divisor; 
 
            // Go through the table again, finding the smallest non-zero 
            // difference between two divisors 
 
                t2 = (Timer_t *)(&timers[0]); 
                for (j = 0;j < CALLBACKS;j++,t2++) { 
 
                    if ((j != i) && (t2->handle)) { 
 
                        long    delta; 
 
                    // Get absolute value of difference between the two 
                    // entries' divisors 
 
                        delta = t2->divisor - t->divisor; 
 
                        if (delta < 0) 
                            delta = -delta; 
 
                    // If non-zero, and smaller than previous smallest 
                    // difference, hold on to it 
 
                        if (delta && (delta < minDivDelta)) 
                            minDivDelta = delta; 
                    } 
                } 
            } 
        } 
 
    // If we no longer need to own the vector, and our ISR is installed, 
    // remove it 
 
        if ((otherCount == 0) && timerhooked) { 
 
#if __BORLANDC__ //DSC 
        _dos_setvect(8,(void interrupt (*)())(timers[0].callBack)); 
#else 
        _dos_setvect(8,(void (interrupt far *)())(timers[0].callBack)); 
#endif 
        timerhooked = FALSE; 
 
        } 
 
    // If we need to own the vector, and our ISR isn't installed, 
    // install it 
 
        if (otherCount && !timerhooked) { 
            _dos_setvect(8,ourintvect); 
            timerhooked = TRUE; 
        } 
 
    // Set timer divisor to minimum divisor 
 
        timerVal = minDiv; 
 
    //  This next bit is here to help out in situations where there are a 
    // couple of fairly slow callbacks set up, and no fast callbacks. If the 
    // only callbacks set up are at 40Hz and 50Hz, and this code weren't 
    // here, the hardware would be set up to generate 50Hz interrupts. 
    // Starting at a time of 0.0, the interrupts would be called at: 
    // Time     40Hz    50Hz 
    // ----     ----    ---- 
    // 0.02             x 
    // 0.04     x       x 
    // 0.06     x       x 
    // 0.08     x       x 
    // 0.10     x       x 
    // 0.12             x 
    // 0.14     x       x 
    // 
    // etc. In other words, four out of every five of the 40Hz callbacks 
    // would be performed with 0.02 seconds in between, and then there 
    // would be a 0.04 second delay before the next call to the 40Hz 
    // callback. Although the 40Hz callback will get called 40 times a 
    // second, the time between calls won't be regular. 
    //  Although this isn't a problem under some circumstances, it can be a 
    // problem under other circumstances, and a nuisance under still other 
    // circumstances. 
    //  This code can increase the timer resolution to about 145.6Hz, 
    // which translates to about 6.87ms (0.00687 seconds). This means that 
    // a particular callback will occur, at most, roughly 3.435ms before 
    // or after it "should". 
    //  Note that by changing the value 0x2000, you can alter the adjustment 
    // frequency that will be used. 
    // 
 
        if (timerVal > minDivDelta) { 
 
            if (minDivDelta >= 0x2000) 
                timerVal = minDivDelta; 
            else if (timerVal > 0x2000) 
                timerVal = 0x2000; 
        } 
 
    // Set the hardware timer value. It's possible that timerVal will be 
    // 0x10000L, more than will fit into a 16-bit word. This is ok, as 
    // programming the timer for 0x0000 is treated by the hardware as 
    // programming the timer for 0x10000. 
 
        programtimer((unsigned)timerVal); 
        curtimerdiv = timerVal; 
} 
 
; 
;   /*\ 
;---|*|----====< findhandleindex >====---- 
;---|*| 
;---|*| This finds an entry in the timers[] array with a handle value 
;---|*| that matches the parameter. Returns -1 if a matching entry 
;---|*| isn't found. 
;---|*| 
;---|*| Copyright (c) 1993 Jason Blochowiak, Argo Games. Used with permission. 
;---|*| 
;   \*/ 
static int findhandleindex(VESAHANDLE han) 
{ 
    int i; 
 
    for (i = 0;i < CALLBACKS;i++) 
        if (timers[i].handle == han) 
            return(i); 
    return(-1); 
} 
 
;   /*\ 
;---|*| end of VESA.C 
;   \*/