home *** CD-ROM | disk | FTP | other *** search
-
- ; /*\
- ;---|*|----====< 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 <stdio.h>
- #include <stdlib.h>
- #include <direct.h>
- #include <string.h>
- #include <dos.h>
- #include <io.h>
- #include <conio.h>
-
- #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 16 // 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 );
- static int readblock ( int, char huge *, 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<CALLBACKS;n++) {
- if (doreg[n][0] == han) {
- ticks = 0;
- break;
- }
- }
-
- // if timer ticks needed, save that fact for a later open
-
- if (ticks) {
- for (n=1;n<CALLBACKS;n++) {
- if (!doreg[n][0]) {
- doreg[n][0] = han;
- doreg[n][1] = ticks;
- break;
- }
- }
- }
- }
-
- // return the completion code
-
- return (cc);
-
- }
-
- ;
- ; /*\
- ;---|*|----====< VESAOpenADevice >====----
- ;---|*|
- ;---|*| 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<CALLBACKS;n++) {
-
- // if this is it, setup for timer ticks
-
- if (doreg[n][0] == han) {
-
- // register wave audio
-
- if ( _fstrncmp(((fpWAVServ)cc)->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<MAXLIBS;n++) {
-
- // only one will match. Free up the memory, then flush the entry
-
- if (han == PatchLibs[n].han) {
-
- // free the patch libs memory
-
- if (PatchLibs[n].fpVAII)
- FreeBuffer((void far *)PatchLibs[n].fpVAII);
-
- // free the patch data memory
-
- if (PatchLibs[n].fpVAID);
- FreeBuffer((void far *)PatchLibs[n].fpVAID);
-
- // close the file
-
- if (PatchLibs[n].fp)
- fclose(PatchLibs[n].fp);
-
- PatchLibs[n].han = 0; // clear out this entry
- PatchLibs[n].ms = 0;
- PatchLibs[n].fp = 0;
- PatchLibs[n].fpVAII = 0;
- PatchLibs[n].fpVAID = 0;
- PatchLibs[n].poffset= 0;
-
- break; // leave the loop
- }
- }
-
- // see if all banks are unloaded. "inuse" == 0 if no more banks loaded
-
- for (n=0;n<MAXLIBS;n++)
- if (PatchLibs[n].han)
- inuse++;
-
- // if all patch banks are freed, then flush the MIDI patch blocks
-
- if ((!inuse) && (FreeMax)) {
-
- for (n=0;n<FreeMax;n++) {
-
- FreeBuffer(PatchPtrs[n]);
-
- PatchPtrs[n] = 0;
- PatchLength[n] = 0;
- PatchState[n] = FALSE;
-
- }
- }
- }
-
- ;
- ; /*\
- ;---|*|----====< VESALoadPatchBank >====----
- ;---|*|
- ;---|*| 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<MAXLIBS;n++) {
-
- if (PatchLibs[n].han == han) // bail if already here
- return(FALSE);
-
- if (PatchLibs[n].han != 0) // count the used slots
- c++;
-
- if (PatchLibs[n].han == 0) // record the empty slots
- if (idx == -1)
- idx = n;
- }
-
- if (c == MAXLIBS) // if all slots full, bail
- return(FALSE);
-
- // open the patch bank in the current directory, PATH or root
-
- _fstrcpy (fname,bnk);
-
- if ((fp = fopen (fname,"rb")) == 0) {
-
- // if there is a path statement, process it...
-
- if ((env = getenv ("PATH")) != 0) {
-
- s = fname; // setup to process each string
-
- while (s) {
-
- // pull the next string from the path
-
- c = 0;
- while (*env)
- if ((*s++ = c = *env++) == ';')
- break;
-
- // if a semicolon stopped us, point to it...
-
- if (c == ';') s--;
-
- *s = 0; // set a terminator
-
- // if there is no other string, break out now...
-
- if (s == fname) {
- s = 0;
- continue;
- }
-
- // if the prior character is not a backslash, append one
-
- if (*(s-1) != '\\') {
- *s++ = '\\';
- *s = 0;
- }
-
- // append the library name now
-
- b = bnk;
- while (*s++ = *b++) ;
-
- // attempt to open the bank now
-
- if ((fp = fopen (fname,"rb")))
- break;
-
- s = fname; // reset for the next iteration
-
- }
-
- }
-
- // if no library after the path search, try the root directory
-
- if (!fp) {
-
- // try the root directory of the current drive
-
- fname[0] = '@' + _getdrive(); // get the current drive
- fname[1] = ':';
- fname[2] = '\\';
- s = &fname[3];
-
- b = bnk;
- while (*s++ = *b++) ;
-
- fp = fopen (fname,"rb");
-
- }
-
- // if after all that, if not opened, it's not there!
-
- if (!fp)
- return(FALSE);
-
- }
-
- // allocate memory for the index block
-
- if ((patchbank = AllocateBuffer(VAIIPLEN)) == 0) {
- fclose (fp);
- return(FALSE);
- }
-
- // load in the first chunk. The first "RIFF" block must be here!
-
- len = readblock (fileno(fp),(pt=patchbank),VAIIPLEN) & 0xffff;
-
- if (cmpstr(&((VAILhdr far *)pt)->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 = AllocateBuffer(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<MAXLIBS;n++) {
-
- // if a device is found, try to load it's patch
-
- if (PatchLibs[n].han == han) {
-
- // if the patch is already loaded, we can just do it...
-
- if (PatchLibs[n].fpVAID) {
-
- // send off the patch - just the data portion, no VAIDhdr
-
- (*PatchLibs[n].ms->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);
- }
-
- ;
- ; /*\
- ;---|*|------------------=============================-------------------
- ;---|*|------------------====< 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<MAXFREE;n++) {
-
- // find it, and mark it free
-
- if (pptr == PatchPtrs[n]) {
- PatchState[n] = FALSE;
- break;
-
- }
- }
- }
-
- ;
- ; /*\
- ;---|*|----====< GetPatchBlock >====----
- ;---|*|
- ;---|*| 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<FreeMax;n++) {
-
- if (len <= PatchLength[n])
-
- return(PatchPtrs[n]);
-
- }
-
- // we didn't find anything, so allocate it from DOS
-
- if (FreeMax < MAXFREE) {
-
- if ((PatchPtrs[FreeMax] = vp = AllocateBuffer(len)) != 0) {
-
- PatchLength[FreeMax] = len;
- PatchState[FreeMax++]= TRUE;
-
- }
- return(vp); // returns the valid pointer
- }
-
- // nothing
-
- return(FALSE);
- }
-
- ;
- ; /*\
- ;---|*|----====< GetPatch >====----
- ;---|*|
- ;---|*| 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<MAXLIBS;n++) {
-
- if (PatchLibs[n].han == han) {
- off = n;
- break;
- }
- }
-
- if (off == -1)
- return(FALSE);
-
- // if the patch is memory, return a pointer to it
-
- if (PatchLibs[n].fpVAID) {
-
- p = PatchLibs[n].fpVAII;
- off = (*p)[patch].poffset - VAIIPLEN;
-
- return((VAIDhdr far *)((char far *)PatchLibs[n].fpVAID + off));
-
- }
-
- // we have to read it from disk!
-
- return(FALSE);
-
- }
-
- ;
- ; /*\
- ;---|*|----====< GetPatchLength >====----
- ;---|*|
- ;---|*| 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<MAXLIBS;n++) {
-
- if (PatchLibs[n].han == han) {
- p = PatchLibs[n].fpVAII;
- return ((*p)[patch].vaidln);
- }
- }
-
- // this handle was'nt registered
-
- return(FALSE);
- }
-
- ; /*\
- ;---|*|----====< ourintvect >====----
- ;---|*|
- ;---|*| 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
- }
-
- ;
- ; /*\
- ;---|*|----====< readblock >====----
- ;---|*|
- ;---|*| read a chunk of the PCM file into the huge buffer
- ;---|*|
- ; \*/
- static 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);
- }
-
-
- ; /*\
- ;---|*|----====< 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
- ; \*/
-
-
-
-
-