/*
 * Programm: Takt (Takterzeugung)
 * Aufgabe : Verschickt in best. Abständen Signale
 * Aufruf  : Takt microsek
 *
 * Auto: make takt
 *
 */

#include "AM.h"

#if 0
# define D(debug) debug
#else
# define D(debug)
#endif

#define ANZ_EMPF 16
#define abbruch SIGBREAKF_CTRL_C

extern struct ExecBase *SysBase;
extern struct Library  *DOSBase;

long				timer, msg;

struct MsgPort		*msgport, *timeport;
struct timerequest	*timereq;

struct { struct Task *task; long sig; }
					empfaenger[ ANZ_EMPF ];


void sendtimereq( long );
void sendtakt( void );
void handlemsg( struct TaktMsg * );
void init( void );
void closedown( void );


main( int argc, char *argv[] )
{
	long sgns, ticks, atol( char * );

	if( argc != 2 ) exit(10);
	ticks = atol( argv[1] );
	
	D(VPrintf("Ticks=%ld\n", &ticks));
	
	init();
	sendtimereq( ticks );
	
	while(1)
	{
		sgns = Wait( abbruch | timer | msg );
		
		if( sgns & timer ) {
			sendtakt();
			sendtimereq( ticks );
			SetSignal( 0L, timer ); }
			
		if( sgns & msg ) {
			handlemsg( (struct TaktMsg *) GetMsg(msgport) );
			SetSignal( 0L, msg ); }

		if( sgns & abbruch )
			closedown();
	}
}


void sendtimereq( long tcks )
{
	D(PutStr("sendtimereq\n"));
	timereq->tr_time.tv_secs  = 0;
	timereq->tr_time.tv_micro = tcks;
	SendIO( timereq );
}

void sendtakt( void )
{
	int i;

	D(PutStr("sendtakt\n"));
	/* allen angeschl. Tasks Signal schicken */
	for( i=0; i<ANZ_EMPF; i++ )
		if( empfaenger[i].task )
			Signal( empfaenger[i].task, empfaenger[i].sig );
}

void handlemsg( struct TaktMsg *msg )
{
	int i;

	switch( msg->tm_op )
	{
		case OP_ADD:
			D(PutStr("OP_ADD\n"));
			for( i=0; i<ANZ_EMPF; i++ )
				if( !empfaenger[i].task )
				{
					empfaenger[i].task = msg->tm_task;
					empfaenger[i].sig  = msg->tm_sig;
					/* Erfolg! */
					msg->tm_task = NULL;
					break;
				}
			break;

		case OP_REM:
			D(PutStr("OP_REM\n"));
			for( i=0; i<ANZ_EMPF; i++ )
				if( empfaenger[i].task == msg->tm_task )
				{
					empfaenger[i].task = NULL;
					empfaenger[i].sig  = 0;
				}
			break;
	}
	
	ReplyMsg( & msg->tm_msg );
}

void init( void )
{
	D(PutStr("init\n"));
	/* Port für Messages */
	msgport = CreatePort( "TAKT", 0 );
	msg = 1L << msgport->mp_SigBit;
	
	/* Timer-Device-Stuff */
	timeport = CreateMsgPort();
	timer = 1L << timeport->mp_SigBit;
	timereq = CreateIORequest( timeport, sizeof(struct timerequest) );
	OpenDevice( TIMERNAME, UNIT_MICROHZ, timereq, 0 );
	timereq->tr_node.io_Command = TR_ADDREQUEST;
	timereq->tr_node.io_Message.mn_ReplyPort = timeport;
}

void closedown( void )
{
	int i;
	struct Message *m;

	D(PutStr("closedown\n"));
	/* Timer-Device schliessen */
	AbortIO( timereq );
	WaitIO( timereq );
	CloseDevice( timereq );
	DeleteIORequest( timereq );
	DeleteMsgPort( timeport );
	
	/* msgPort schliessen (auch für angeschl. Tasks, damit die keine
	 * keine OP_REMs schicken beim folgenden Ctrl-C)
	 */
	Forbid();
	 while( m=GetMsg(msgport) ) ReplyMsg(m);
	 DeletePort( msgport );
	Permit();
	
	/* allen angeschl. Tasks CTRL-C schicken */
	for( i=0; i<ANZ_EMPF; i++ )
		if( empfaenger[i].task )
			Signal( empfaenger[i].task, SIGBREAKF_CTRL_C );
	
	exit(0);
}
