/*
 ***************************************************************************
 *   		Polling the signals and fill in the current
 *	          buffer with the inter-impulses intervals
 *
 * Polling is performed every time ticks. The PC system timer is
 * programmed to raise interrupts at a given frequency (every
 * Polling_period microseconds). On interrupt the program increments
 * the ticks counters and polls the channels. Once the impulse has occured,
 * the value of the corr. counter is written down to the buffer
 * (along with the channel no info), and the counter is dropped.
 *
 * Important notes
 * - No stack checking should be performed on entry to the interrupt
 * handling routines and all of their subroutines.
 * - Don't forget to send the End-of-Interrupt message to the Interrupt
 * control chip. Otherwise all the interrupts next to this would be
 * disabled.
 * - The impulse width is of the similar order as the polling
 * interval. Therefore, the same impulse can be counted twice as if there
 * were two separate impulses. We assume the impulsation frequency is
 * not so high so that it is unlikely that two impulses would occur in
 * a tick time interval. Therefore, if two successive polls of a channel
 * notice impulses, the second impulse is considered to be the
 * continuation of the previous and isn't counted
 * - Counting intervals is started from the first impulse occured
 * on the 1. channel
 * - PC system timer is set up by BIOS to interrupt the CPU in every
 * 1/18.2 sec. We need other value for the timer tick. It is good practice
 * to inquire the timer service about the current timer tick value and
 * remember it to restore the status quo on exit. However, inquiring
 * timer service about the current timer tick value on AT computers
 * gives stupid results, it ain't work as described. So we are made to use
 * our a priori knowledge that BIOS sets the timer latch register to
 * 0xffff.
 *
 ***************************************************************************
 */

#include <dos.h>
#include "stdio.h"
#include "assert.h"
#include "timer.h"
#define FILLING_BUFFERS
#include "impulses.h"
#include "filling.h"

#define Timer_no		0	/* It is this timer port that	*/
					/* is connected to IRQ		*/
#define Timer_interrupt_no	8	/* Interrupt no for the timer	*/

#define Max_no_channels_handled	4

static int Ticks_counter1;	/* Time counters for channels		*/
static int Ticks_counter2;
static int Ticks_counter3;
static int Ticks_counter4;


				/* Interrupt routine and all else that are*/
				/* called by it should have stack checking*/
				/* disabled !!!				  */
#pragma -N- -k- -v-

/*
 *--------------------------------------------------------------------------
 *	     		Current buffer switching
 */

				/* Prepare the buffer for filling out	*/
void set_the_buffer_current(struct BUFFER far * bp)
{
  Current_buffer      = bp;
  No_elements_to_fill = Current_buffer->size;
  Current_ptr         = Current_buffer->data;
  assure( Current_buffer->actual_length == 0,
	"Buffer to be set current is not empty!");
}

void push_current_buffer()
{
  assure(No_elements_to_fill >= 0,
	"Push_current_buffer: Buffer overflowed");
  Current_buffer->actual_length = Current_buffer->size - No_elements_to_fill;
  Status = 'W';			/* Ready to write		*/
}

void switch_buffers()
{
  push_current_buffer();
  set_the_buffer_current(Current_buffer->next);
}

/*
 *--------------------------------------------------------------------------
 *			Timer interrupt handlers
 */

					/* Handler for actual no of channels*/
static void interrupt (*Actual_handler)();

			/* Impulse counting will started with the first	*/
			/* impulse occured on channel 1			*/
static void interrupt synchronizing_with_channel_1()
{
			/* Poll the channel 1				*/
			/* The following three stmts should		*/
			/* be written in the rigorous C as single stmt	*/
			/* if( (inportb(Poll_port1_no) & Poll_flag1) != 0 ) */
  _DX = Poll_port1_no;
  __emit__(0xec);		/* inp al,dx - input from DX, result in AL*/
  if( (_AL & Poll_flag1) != 0 )
  {				/* Have caught the synchro-impulse	*/
    Ticks_counter1 = 0;
    Ticks_counter2 = 0;
    Ticks_counter3 = 0;
    Ticks_counter4 = 0;
			/* The following statements are effectively	*/
			/* the same as to write				*/
			/* setvect(Timer_interrupt_no,Actual_handler);	*/
			/* or                                      	*/
			/* [4*Timer_interrupt_no] <- Actual_handler	*/
    _DI = 4*Timer_interrupt_no;	/* 4 is the length for the interrupt vector*/
    __emit__(0x1e);		/* push ds			*/
    _SI = FP_OFF(&Actual_handler);
    _DS = FP_SEG(&Actual_handler);
    __emit__(0x06);		/* push es			*/
    __emit__(0x31,0xc0,0x8e,0xc0);/* xor  ax,ax; mov es,ax	*/
    __emit__(0xa5);		/* movsw  ; move offset then segment	*/
    __emit__(0xa5);		/* movsw  ; portions from the		*/
				/* Actual_handler to the interr vector	*/
    __emit__(0x07);		/* pop  es			*/
    __emit__(0x1f);		/* pop  ds			*/
    Status = 'I';
  }
		/* Tell interrupt controller we are over with the interrupt*/
		/* The codes given below are the same as	*/
		/*  outportb(0x20,0x20);			*/
  __emit__(0xb0,0x20);		/* mov al,20	*/
  __emit__(0xe6,0x20);	     	/* out 20,al	*/
}


static void interrupt handle_1_channel()
{
  Ticks_counter1++;
			/* Poll the channel 1				*/
			/* The following three stmts should		*/
			/* be written in the rigorous C as single stmt	*/
			/* if( (inportb(Poll_port1_no) & Poll_flag1) != 0 ) */
  _DX = Poll_port1_no;
  __emit__(0xec);		/* inp al,dx - input from DX, result in AL*/
  if( (_AL & Poll_flag1) != 0 )
  {				/* There occured an impulse		*/
    if( Ticks_counter1 != 1 )
    {
      *Current_ptr++ = Ticks_counter1;
      Ticks_counter1 = 0;
      if(--No_elements_to_fill <= 0)
	switch_buffers();
    }
  }
		/* Tell interrupt controller we are over with the interrupt*/
		/* The codes given below are the same as	*/
		/*  outportb(0x20,0x20);			*/
  __emit__(0xb0,0x20);		/* mov al,20	*/
  __emit__(0xe6,0x20);	     	/* out 20,al	*/
}


static void interrupt handle_2_channels()
{
  Ticks_counter1++;
  Ticks_counter2++;
			/* Poll the channel 1				*/
			/* The following three stmts should		*/
			/* be written in the rigorous C as single stmt	*/
			/* if( (inportb(Poll_port1_no) & Poll_flag1) != 0 ) */
  _DX = Poll_port1_no;
  __emit__(0xec);		/* inp al,dx - input from DX, result in AL*/
  if( (_AL & Poll_flag1) != 0 )
  {				/* There occured an impulse		*/
    if( Ticks_counter1 != 1 )
    {
      *Current_ptr++ = Ticks_counter1 | Channel1_impulses_flag;
      Ticks_counter1 = 0;
      if(--No_elements_to_fill <= 0)
	switch_buffers();
    }
  }
			/* Poll the channel 2				*/
  _DX = Poll_port2_no;
  __emit__(0xec);		/* inp al,dx - input from DX, result in AL*/
  if( (_AL & Poll_flag2) != 0 )
  {				/* There occured an impulse		*/
    if( Ticks_counter2 != 1 )
    {
      *Current_ptr++ = Ticks_counter2 | Channel2_impulses_flag;
      Ticks_counter2 = 0;
      if(--No_elements_to_fill <= 0)
	switch_buffers();
    }
  }
		/* Tell interrupt controller we are over with the interrupt*/
		/* The codes given below are the same as	*/
		/*  outportb(0x20,0x20);			*/
  __emit__(0xb0,0x20);		/* mov al,20	*/
  __emit__(0xe6,0x20);	     	/* out 20,al	*/
}

/*
 *--------------------------------------------------------------------------
 * 			Initialization routines
 */
#pragma -N -k				/* Restore standard stack checking*/

typedef void interrupt (*INTER_HANDLER)();

static INTER_HANDLER Handle_x_channels[Max_no_channels_handled+1] =
	{ (INTER_HANDLER)0, handle_1_channel,  handle_2_channels,
		     handle_2_channels, handle_2_channels };


static char Interrupt_handling_flag = 'N';
			/* 'N' - not initialized			*/
			/* 'R' - No_channels_polled has been set	*/
			/* 'I' - initialized and working		*/

			/* Set up a new value to the No_channels_polled	*/
			/* from the parameter given if it seems fit	*/
			/* return -1 if the no of channels given is	*/
			/* wrong					*/
			/* Otherwise, return 0				*/
int set_no_channels_to_poll(const int no_channels)
{
  if( no_channels <= 0 || no_channels > Max_no_channels_handled )
    return -1;
  No_channels_polled = no_channels;
  assure( Interrupt_handling_flag != 'I',
	  "set_no_channels_to_poll: Channels are being polled. Can't change on fly");
  Interrupt_handling_flag = 'R';
  return 0;
}

			/* Return the string describing the signal	*/
			/* to poll					*/
const char * poll_signal_description(const int channel_no)
{
  assure( Interrupt_handling_flag != 'N',
	  "poll_signal_description: You havn't set no channels to poll");
  assure( channel_no > 0 && channel_no <= No_channels_polled,
	  "poll_signal_description: invalid channel no specified");
  return Poll_channels_descr[channel_no];
}

			/* Set up timing and polling routine		*/
void init_reset_timer_interrupt(flag)
const char flag;                /* 'I', or 'R'				*/
{
  static INTER_HANDLER old_timer_int_handler;
  static unsigned old_timer0_latch;

  if( flag == 'I' )		/* Initialization			*/
  {
    assure( Interrupt_handling_flag == 'R',
	"Timer is working or no channels wasn't set");
    old_timer_int_handler = getvect(Timer_interrupt_no);
    old_timer0_latch = 0xffff;  /* Don't rely on "get_latch" function on AT*/
				/* Timer_control("get_latch",Timer_no,0);*/
    Timer_control("set_period",Timer_no,Polling_period);
    Status = 'S';
    Actual_handler = Handle_x_channels[No_channels_polled];
    setvect(Timer_interrupt_no,synchronizing_with_channel_1);
    Interrupt_handling_flag = 'I';
    return;
  }
  else if( flag == 'R' )	/* Reset to the original state		*/
  {
    assure( Interrupt_handling_flag == 'I',
	"Attempt to reset timer, but it hasn't been initiated");
    setvect(Timer_interrupt_no,old_timer_int_handler);
    Timer_control("set_latch",Timer_no,old_timer0_latch);
    Interrupt_handling_flag = 'R';
    return;
  }
  _error("init_reset_timer: Can't understand the flag '%c'",flag);
}

