/******
 *
 *	T I M E _ I S R . C          by Jan Ceuleers, The Experimenter Board, 1992
 *	                                           part of the ISDN FOSSIL project
 *
 *	This module contains the timer hardware interrupt handler.
 *
 *	Due to certain complications, the ISDN driver must not be called from
 *	this module (directly or indirectly). This means that if the code within
 *	this module establishes that an ISDN-related action must be performed, it
 *	must request this action to be performed by the polling functions (i.e.
 *	pollIsdnDriver). This is done by setting flags in the port record.
 *
 ******/

#include <dos.h>
#include "fos_isdn.h"

//define LOCALSTACK

#ifdef LOCALSTACK
	char localStack[512];		// Local stack --> non-reentrant
	char far *localStackPointer = localStack + sizeof(localStack) - 2;
	char far *applicationStackPointer = (char far *)0L;
#endif

int alreadyBusy = 0;		// Non-reentrancy flag
int ticksSkipped = 0;		// Indication of number of ticks that were skipped
extern volatile int isdnDriverBusy;
extern volatile int ticksSinceLastIncomingCallPoll;

#pragma check_pointer(off)
#pragma check_stack(off)
void interrupt far cdecl timer_isr()
{
	_disable();

	ticksSinceLastIncomingCallPoll++;

	if ((!alreadyBusy++) && (!isdnDriverBusy))
	{
		#ifdef LOCALSTACK
			// Switch to local stack
			{
				_asm
				{
					push bp
					mov  word ptr applicationStackPointer[2],ss
					mov  word ptr applicationStackPointer[0],sp
					mov  ss,word ptr localStackPointer[2]
					mov  sp,word ptr localStackPointer[0]
					mov  bp,sp
				}
			}
		#endif

		_enable();

		{
			unsigned portIndex;
			port_t *pport;

			for (portIndex = 0, pport = ports;
			     portIndex < number_of_ports;
			     portIndex++, pport++)
			{
				if (P(initialized))
				{
					P(ticksSinceLastTx)++;
					P(ticksSinceLastPoll)++;
					P(idleCount) = 1;

					switch (P(call.state))
					{
						case 0:
							break;

						case 1:		// Incoming call detected
							if (--P(call.timer) <= ticksSkipped)
							{
								// It's time to send another RING
								P(modem.responseCode) = RSP_RING;
								emitResponseCode(pport);
								P(call.timer) = 73-ticksSkipped;		// about 4 seconds
								P(call.checkAutoAnswer) = 1;
							}
							else
							{
								P(call.timer) -= ticksSkipped;
							}
							break;

						case 2:		// Call proceeding
							break;

						case 3:		// Outgoing call pending (awaiting connection)
							if (--P(call.timer) <= ticksSkipped)
							{
								// timeout: no response within set time limit
								P(call.hangupRequested) = 1;
								P(modem.responseCode) = RSP_NO_CARRIER;
								emitResponseCode(pport);
							}
							else
							{
								P(call.timer) -= ticksSkipped;
							}
							break;
					}
				}
			}
		}

		#ifdef LOCALSTACK
			// Switch back to application stack
			{
				_asm
				{
					cli
					mov  ss,word ptr applicationStackPointer[2]
					mov  sp,word ptr applicationStackPointer[0]
					pop  bp
					sti
				}
			}
		#endif

		ticksSkipped = 0;
	}
	else
	{
		++ticksSkipped;
	}

	alreadyBusy--;

	_chain_intr(old_clock_tick_ISR);
}
