developer-toolkit-v1.01/installed/INTERR.C

UTF-8 rendering of a DOS-encoded (CP437 / CRLF) file. Byte-for-byte original: INTERR.C.


/**************************************************************************
                   
Module name:   Interr.C

Version:		0.09

Author:        Francois Rousseau

Date:          november 1992

Description:	

*****************************************************************************/

/****************************************************************************
								Module History
21/11/92       0.09
*****************************************************************************/

/****************************************************************************
                               Includes
*****************************************************************************/

#ifdef TURBO
#pragma	hdrfile	interr.sym
#endif

#include   <stdio.h>
#include   <stdlib.h>
#include   <dos.h>

#include   "global.H"
#include   "Interr.H"
#include   "control.H"
#include   "timer.H"
#include   "midi.H"
#include   "wave.H"

#ifdef TURBO
#pragma	hdrstop
#undef		inportb					// Protection against fast access
#undef		outportb
#endif

/*****************************************************************************/

PRIVATE	unsigned int near mySP;
PRIVATE	unsigned int near mySS;
PRIVATE	unsigned int near oldSP;
PRIVATE	unsigned int near oldSS;

/*
 *	Redirection table (function pointers) to external modules:
 *		MIDI, Wave, timer, etc. (listed in ctrldrv.h)
 */

PRIVATE	FastAccessTable driverTable[ADLIB_MAX_NUMBER_ID + 1];

/*
 *	Mask for 8259
 */

PRIVATE	BYTE intMasks[] = { 
			1 << 3, 1 << 4, 1 << 5, 1 << 7,
			1 << 10 -8, 1 << 11 -8, 1 << 12 -8, 1 << 15 -8};

/*
 *	This module needs access to on board chips. The initialisation
 *	routine gets the basic address and stores it in those variables.
 */

PRIVATE    WORD   controlIoPort = 0x0000;
PRIVATE    WORD   opl3IoPort 	 = 0x0000;
PRIVATE    WORD   mmaIoPort  	 = 0x0000;
PRIVATE    WORD   delayIO    = 0x0080;

/*
 *	Used for level 1 to keep track of the current gold interrupt selection.
 * the value used in software is set with an environment variable in the
 *	autoexec.bat file.
 */

PRIVATE	BYTE	interrupt8259;

/*
 *	Interrupt routines pointers for installation and de-installation
 */

PRIVATE	void		interrupt	ProcessInterrupt();
PRIVATE	void		(interrupt far *oldVect)();

/*****************************************************************************/

/*
 *	Synopsis:		CallbackProc FastDoNothing()
 *
 *	Description:	Used as a default value for all interrupt service routines
 *
 * Arguments:		None.
 *
 *	Returned value: None
 *                 
 */

PRIVATE
CallbackProc FastDoNothing()
	{
	}

/*
 *	Synopsis:		WORD	InitInterruptService(WORD baseAddress)
 *
 *	Description:	Used to install the GOLD interrupt routine.
 *
 * Arguments:		WORD baseAddress
 *						The port address of the mma & opl3 chip and
 *						control chip are passed as argument.
 *
 *	Return Value:	0	no error
 *					1	error		
 *                 
 */

WORD	InitInterruptService(WORD baseAddress)
	{
	WORD  	port8259;
	BYTE	theByte;
	BYTE	mask;
	char	*s;
   int     i;
	BYTE	vector;
	BYTE 	vecs[] = { 11, 12, 13, 15, 0x72, 0x73, 0x74, 0x77 };
	BYTE 	IRQS[] = { 0, 0, 0, 0, 1, 2, 0, 3, 0, 0, 4, 5, 6, 0, 0, 7 };

	opl3IoPort 		= baseAddress;
	controlIoPort 	= baseAddress + 2;
	mmaIoPort 		= baseAddress + 4;

   for (i = 0; i < ADLIB_MAX_NUMBER_ID + 1; i++) {
	    driverTable[i].intFunc = FastDoNothing;
		}

	/*
	 * 	Set interrupt vector 
	 */

	if (gssLevel == level2) {
		/*
		 *	IRQ3 = 0, IRQ7 = 3 
		 */
		interrupt8259 = CtGetInterruptLineNbr();
		}
	if (gssLevel == level1) {
		s = getenv("GOLDINTSEL");
		if ((s == NULL) OR (! isxdigit(s[0]))) {
		   	printf("Environment variable for Gold interrupt not valid: %s\n", s);
			/*
			 * 	Default interrupt IRQ 5
			 */
		   	s = "2";
			}
		interrupt8259 = IRQS[Hexa(s)];
		}

	asm	pushf				// Disable interrupt
	asm	cli

	vector = vecs[interrupt8259];

   oldVect = getvect(vector);
   setvect(vector, ProcessInterrupt);

	if (interrupt8259 < 4) 
		port8259 = 0x0020;
	else port8259 = 0x00A0;

	/*
	 *	Enable ctrl chip interrupt by putting a 0 at the appropriate
	 *	place in one of the two 8259.
	 */

	theByte = inportb(port8259 + 1);	   
	outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);
	mask = theByte & ~intMasks[interrupt8259];
	outportb(port8259 + 1, mask);
	outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);

	asm	popf

	return(0);
	}


/*
 *	Synopsis:		WORD	RemoveInterruptService()
 *
 *	Description:	This routine will restore the context before the 
 *					execution of this program.
 *
 *	Argument:		no arguments
 *
 *	Return Value:	0	no error
 *					1	error
 */

WORD	RemoveInterruptService()
	{
	WORD  	port8259;
	BYTE	theByte;
	BYTE	mask;

	asm	pushf
	asm	cli

   setvect(CtGetInterruptRoutine(), oldVect);

	if (interrupt8259 < 4) 
		port8259 = 0x0020;
	else port8259 = 0x00A0;

	/*
	 *	Enable ctrl chip interrupt by putting a 1 at the appropriate
	 *	place in one of the two 8259.
	 */

	theByte = inportb(port8259 + 1);	   
	outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);
	mask = theByte | intMasks[interrupt8259];
	outportb(port8259 + 1, mask);
	outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);

	asm	popf
	return(0);
	}

CallbackPtr 
SetDriverCallback(AdLibSubDriverID driverID, CallbackPtr newProc)

	{
	driverTable[driverID].intFunc = newProc;
   return(newProc);
   }

CallbackPtr 
ResetDriverCallback(AdLibSubDriverID driverID)

	{
   driverTable[driverID].intFunc = FastDoNothing;
   return(FastDoNothing);
   }

/*************************************************************************/

#define	maxReentry 		18				// No reason, just enough...

static unsigned int 	near thisSP;
static unsigned int 	near reentryCount= 0;
static BYTE			near ctStatus[maxReentry];
static BYTE			near status[maxReentry];
static BYTE			near MMA[maxReentry];
static BYTE			near OPL3[maxReentry];
static BYTE			near SCSI[maxReentry];
static BYTE			near PHONE[maxReentry];

/*
 *	patch against filled 0's when receiving MIDI
 */

static BYTE			near again[maxReentry];	

static BYTE			near loopCond[maxReentry]; // To implement level 1 & 2

#ifndef TURBO
static BYTE	tempStatus;
#endif

/*
 *	Synopsis:			ProcessInterrupt()
 *
 *	Description:		This is the main interrupt routine generated on the PC
 *						bus by the Control Chip. Interrupts may either come
 *						from the OPL3 or MMA chips. Level 1 only supports
 *						interrupts from the MMA.
 *
 *						SCSI interrupts are not handles,they should be handled by
 *						the CD-ROM driver.
 *
 *	Argument:			no arguments
 *
 * Returned values:	none.
 *
 */	

void   interrupt	ProcessInterrupt()
	{
	/*
	 *	Prepare context switching
	 */

	asm 	mov		ax, ds
	asm 	mov		es, ax

   thisSP = mySP 	- (512 *  reentryCount++);
   asm     mov     DS:oldSS, ss
   asm     mov     DS:oldSP, sp
   asm     mov     ss, DS:mySS
   asm     mov     sp, DS:thisSP

	/*
	 *	Enable reentry (by enabling interrupt)
	 */

	// asm		sti          

	/*
	 *	To provide support for level_1 and 2. This loopCond simulates
	 *	in both case a Do While control structure. It may look complex...
	 */
	loopCond[reentryCount] = 0xF0;

   MMA[reentryCount] = 0x00;
   again[reentryCount] = 0x00;			

	/*
	 *	In level 1 there won't be any of these interruption sources
	 */

	OPL3[reentryCount] = 0x00;
	SCSI[reentryCount] = 0x00;
	PHONE[reentryCount] = 0x00;

	/*
	 *	It's just like a Do While at this point. The while loop is used
	 *	for spped while processing fast incoming interrupt.
	 */

	while (~loopCond[reentryCount] & 0x07) {

		/*
		 * 	If running level 2 then we need a whille loop over the content
		 *	of control chip's status register. Make sure to assign the loopCond
		 *	variable for the while loop condition above. The level 1 while
		 *	loop condition is set at the end of the while block ( { } ).
		 */
		 
		if (gssLevel == level2) {
			loopCond[reentryCount] = GetControlRegister(-1);
			ctStatus[reentryCount] = loopCond[reentryCount];
			MMA[reentryCount] 	= (ctStatus[reentryCount] & 0x02) >> 1;
			PHONE[reentryCount] = (ctStatus[reentryCount] & 0x04) >> 2;
			OPL3[reentryCount] 	= (ctStatus[reentryCount] & 0x01);
			}

		/*
		 *	In level 1, interrupts will be coming from the MMA chip only. To
		 *	keep the following code functionnal for the two level we force
		 *	this variable to 0.
		 */

		if (gssLevel == level1) {
			MMA[reentryCount] 	= 0;
			}
	       	
		/*
	 	 *  First find which interrupt to process
	 	 *	Check Wave sampler first for speed
	 	 */

		if (! MMA[reentryCount]) {
			/*****************************************************
			 *****************************************************
			 *
			 * MIDI reception requires a while loop over the MMA
			 *
			 *****************************************************
			 *****************************************************
			 */
			status[reentryCount] = inportb(mmaIoPort);
			outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);

			if (status[reentryCount] & 0x03) {
				#ifdef TURBO			
					_BL = status[reentryCount];
				#else
					tempStatus = status[reentryCount];
					asm	mov	bl, tempStatus
				#endif
				(*driverTable[ADLIB_WAVE_DRIVER_ID].intFunc)();
				}
			if (status[reentryCount] & 0x70) {
				#ifdef TURBO
					_BL = status[reentryCount];
				#else
					tempStatus = status[reentryCount];
					asm	mov	bl, tempStatus
				#endif
					
				(*driverTable[ADLIB_TIMER_DRIVER_ID].intFunc)();
				}
			if (status[reentryCount] & 0x80) {
				/*
			 	 * 	Special case of overrun on MIDI or Wave...
			 	 */
				#ifdef TURBO
					_BL = status[reentryCount];
				#else
					tempStatus = status[reentryCount];
					asm	mov	bl, tempStatus
				#endif
				(*driverTable[ADLIB_MIDI_DRIVER_ID].intFunc)();
				}
			if (status[reentryCount] & 0x0C) {  
				#ifdef TURBO
					_BL = status[reentryCount];
				#else
					tempStatus = status[reentryCount];
					asm	mov	bl, tempStatus
				#endif
				(*driverTable[ADLIB_MIDI_DRIVER_ID].intFunc)();
				if (status[reentryCount] & 0x04) {
					again[reentryCount] = inportb(mmaIoPort);
					outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);
					}
				}
			}

		/********************************************************************
		 *				OPL3, PHONE interruptions
		 ********************************************************************/

		if (gssLevel == level2) {
			if (! OPL3[reentryCount]) {

				if (inportb(opl3IoPort) & 0x60) {
					outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);
					/*
				 	* Don't process MMA interrupt a second time with _BL == 0
				 	*/
					#ifdef TURBO
						_BL = 0x00;
					#else
						asm	xor	bl, bl
					#endif
					(*driverTable[ADLIB_TIMER_DRIVER_ID].intFunc)();
					}

				/*
	 			 *	Reset last timer interrupt in OPL3
				 */

				outportb(opl3IoPort, 0x04);
				outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);
				outportb(opl3IoPort + 1, 0x80);
				outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);
				}

			if (! PHONE[reentryCount]) {
				CtGetHangUpPickUpTelephoneLine();
				}
			} /* End of level 2 */

		if (gssLevel == level1) {
			loopCond[reentryCount] = 0x0F;
		}

	}	/* End of while loop	*/

   --reentryCount;

   asm     mov     ss, DS:oldSS
   asm     mov     sp, DS:oldSP

	/********************************************************************
	 *				Chain Interrupt
	 ********************************************************************/

	(*oldVect)();

	/********************************************************************
	 *				Reset interrupt under service (8259)
	 ********************************************************************/
	asm pushf
   asm cli


	if (interrupt8259 > 3) {
		outportb(0x00A0, 0x20);
		outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);
		}

	outportb(0x0020, 0x20);
	outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0); outportb(delayIO, 0);

   asm popf
	}