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;n wsname,"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;n type[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;n msPreLoadPatch) ( 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 ; \*/