/*
 *
 *	DISCLAIMER:
 *
 *	This program is provided as a service to the programmer
 *	community to demonstrate one or more features of the Amiga
 *	personal computer.  These code samples may be freely used
 *	for commercial or noncommercial purposes.
 * 
 * 	Commodore Electronics, Ltd ("Commodore") makes no
 *	warranties, either expressed or implied, with respect
 *	to the program described herein, its quality, performance,
 *	merchantability, or fitness for any particular purpose.
 *	This program is provided "as is" and the entire risk
 *	as to its quality and performance is with the user.
 *	Should the program prove defective following its
 *	purchase, the user (and not the creator of the program,
 *	Commodore, their distributors or their retailers)
 *	assumes the entire cost of all necessary damages.  In 
 *	no event will Commodore be liable for direct, indirect,
 *	incidental or consequential damages resulting from any
 *	defect in the program even if it has been advised of the 
 *	possibility of such damages.  Some laws do not allow
 *	the exclusion or limitation of implied warranties or
 *	liabilities for incidental or consequential damages,
 *	so the above limitation or exclusion may not apply.
 *
 */

/* SIMPLE TIMER EXAMPLE PROGRAM: 
 *
 * Includes dynamic allocation of data structures needed to communicate
 * with the timer device as well as the actual device IO
 *
 * Author:  Rob Peck, 12/1/85 */

#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/memory.h"
#include "exec/interrupts.h"
#include "exec/ports.h"
#include "exec/libraries.h"
#include "exec/io.h"
#include "exec/tasks.h"
#include "exec/execbase.h"
#include "exec/devices.h"
#include "devices/timer.h"

long TimerBase;		/* to get at the time comparison functions */

main()
{
	LONG error;
	LONG days, minutes,seconds;
	struct timeval mytimeval;
	struct timeval oldtimeval;	/* save what system thinks is
					 * the time.... we'll advance it
					 * temporarily 
					 */
	printf("\ntimer test");

	TimeDelay(2,0,0);
	printf("\nAfter 2 seconds delay");

	TimeDelay(4,0,0);
	printf("\nAfter 4 seconds delay");

	TimeDelay(0,500000,0);	/* 500,000 seconds = 1/2 second */
	printf("\nAfter 1/2 second delay");

	printf("\n\n");
	error = Execute("date",0,0);	/* normal system startup file 
					 * opens dos.library, so it is
					 * ok in such cases to use dos calls */

	GetSysTime(&oldtimeval);
	printf("\nCurrent system time is %ld current seconds", 
		oldtimeval.tv_secs);	

	printf("\nSetting a new system time");

	days = 1000 + oldtimeval.tv_secs / (24*60*60);
	minutes = (oldtimeval.tv_secs % (24*60*60)) / 60;
	seconds = (oldtimeval.tv_secs % (24*60*60)) % 60;

	SetNewTime(days, minutes, seconds);
	/* (if user executes the AmigaDOS DATE command now, he will
	 * see that the time has advanced something over 1000 days */

	printf("\n\n");
	error = Execute("date",0,0);	/* normal system startup file 
					 * opens dos.library, so it is
					 * ok in such cases to use dos calls */
	GetSysTime(&mytimeval);
	printf("\nCurrent system time is %ld.%06ld", 
		mytimeval.tv_secs,mytimeval.tv_micro);	

	GetSysTime(&mytimeval);
	printf("\nCurrent system time is %ld.%06ld", 
		mytimeval.tv_secs,mytimeval.tv_micro);	

	GetSysTime(&mytimeval);
	printf("\nCurrent system time is %ld.%06ld", 
		mytimeval.tv_secs,mytimeval.tv_micro);	

	/* added the microseconds part to show that time keeps
	 * increasing even though you ask many times in a row */

	printf("\nResetting to former time");

	days = oldtimeval.tv_secs / (24*60*60);
	minutes = (oldtimeval.tv_secs % (24*60*60)) / 60;
	seconds = (oldtimeval.tv_secs % (24*60*60)) % 60;

	SetNewTime(days, minutes, seconds);

	GetSysTime(&mytimeval);
	printf("\nCurrent system time is %ld.%06ld", 
		mytimeval.tv_secs,mytimeval.tv_micro);	

	TimerBase = GetTimerBase();	
		/* just shows how to set up for using 
		 * the timer functions, does not demonstrate
		 * the functions themselves.  (TimerBase must
		 * have a legal value before AddTime, SubTime or CmpTime
		 * are performed.
		 */
}

/* *********************************************************************** */
/*	Timer function - timedelay(seconds,microseconds)

	Your task is put to sleep for the specified time interval.

	If seconds > 0, then UNIT_VBLANK is used, delay is in multiples of
	60ths of a second.  If seconds < 0, then UNIT_MICROHZ is used for 
	more precision.    

	Returns value of 0 if no errors, nonzero (and no task sleeping)

	Notice that since this is a multi-tasking system, the delays
	shown here must be considered to be only approximate.

	Also note that this function is used primarily to show how
	a timer device is accessed, including the creation of the 
	message port and a message structure (IOStdReq).   Note that
	there is a Delay(interval) function already in the DOS.library.
	(See the DOS developer's manual for details).

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

extern struct MsgPort *CreatePort();
extern struct IORequest *CreateExtIO();

struct timerequest 
*PrepareTimer(precision)
SHORT precision;
{
	/* return a pointer to a time request.  If any problem, return NULL */

	int error;
	SHORT whichunit;

	struct MsgPort *timerport;
	struct timerequest *timermsg;
	
        timerport = CreatePort(0,0);
        if (timerport == NULL) 
		return(NULL);	/* Error during CreatePort */

	timermsg = (struct timerequest *)CreateExtIO(
					timerport,sizeof(struct timerequest));
	if (timermsg == NULL)
		{
		DeletePort(timerport);
		return(NULL);	/* Error during CreateExtIO */
		}
	
	if(precision)	/* if true, use precision timer  ( under 1 second ) */
		whichunit = UNIT_MICROHZ;
	else
		whichunit = UNIT_VBLANK;

	error = OpenDevice(TIMERNAME, whichunit, timermsg, 0);
	if (error != 0)
		{
		DeleteExtIO(timermsg,sizeof(struct timerequest));
		DeletePort(timerport);
		return(NULL);	/* Error during OpenDevice */
		}
	return(timermsg);
}

int
GetTimerBase()
{
	int tbase;
	struct timerequest *tr;
	tr = PrepareTimer();
	tbase = (int)tr->tr_node.io_Device;		
	DeleteTimer(tr);
	return(tbase);
}

int 
TimeDelay(seconds,microseconds,precision)
		/* more precise timer than AmigaDOS Delay() */
ULONG seconds,microseconds;
int precision;
{
	int precise;
	struct timerequest *tr;
	if(seconds < 0 || precision != 0)
				/* do delay in terms of microseconds */ 
		precise = TRUE; /* yes, use the precision timer.     */
	else 
		precise = FALSE; /* no, not necessary */

	tr = PrepareTimer(precise);
					/* get a pointer to an initialized
					 * timer request block */
	if(tr == NULL) return(-1);	/* any nonzero return says timedelay
					 * routine didn't work. */
	WaitForTimer(tr,seconds,microseconds);

	DeleteTimer(tr);	/* deallocate temporary structures */
	return(0);
}			/* end of timedelay */


int
WaitForTimer(tr,seconds,microseconds)
ULONG seconds,microseconds;
struct timerequest *tr;
{
	tr->tr_node.io_Command = TR_ADDREQUEST;   /* add a new timer request */
        tr->tr_time.tv_secs =  seconds;        	/* seconds */
        tr->tr_time.tv_micro = microseconds; 	/* microseconds */
        DoIO( tr );             		/* post request to the timer */
						/* goes to sleep till done */
	return(0);
}

int
SetNewTime(day,min,sec)
LONG day, min,sec;	/* days since 1 Jan 78 plus minutes */
{
        struct timerequest *tr;
        tr = PrepareTimer(TRUE);  /* MUST use Precise timer for this */
        if(tr == 0) return(-1);   /* non zero return says error */ 

	tr->tr_node.io_Command = TR_SETSYSTIME;
	tr->tr_time.tv_secs = (24 * 60 * 60 * day) + (60 * min) + sec;
	tr->tr_time.tv_micro = 0;
	DoIO( tr );

	DeleteTimer(tr);
	return(0);
}


int
GetSysTime(tv)
struct timeval *tv;
{
	struct timerequest *tr;
	tr = PrepareTimer(TRUE);	/* MUST use Precise timer for this */
	if(tr == 0) return(-1);		/* non zero return says error */
 
	tr->tr_node.io_Command = TR_GETSYSTIME;
	DoIO( tr );

	tv->tv_secs = tr->tr_time.tv_secs;
	tv->tv_micro = tr->tr_time.tv_micro;

	DeleteTimer(tr);
	return(0);

}

int
DeleteTimer(tr)
struct timerequest *tr;
{
	struct MsgPort *tp;

	tp = tr->tr_node.io_Message.mn_ReplyPort;
	if(tr != 0)
	{
		CloseDevice(tr);
		DeleteExtIO(tr,sizeof(struct timerequest));
	}
	if(tp != 0)
		DeletePort(tp);
	return(0);		
}



/***********************************************************************
*
*	Exec Support Function -- Extended IO Request
*
***********************************************************************/

extern APTR AllocMem();

/****** exec_support/CreateExtIO **************************************
*
*   NAME	
*	CreateExtIO() -- create an Extended IO request
*
*   SYNOPSIS
*	ioReq = CreateExtIO(ioReplyPort,size);   
*
*   FUNCTION
*	Allocates memory for and initializes a new IO request block
*	of a user-specified number of bytes.
*
*   INPUTS
*	ioReplyPort - a pointer to an already initialized
*		message port to be used for this IO request's reply port.
*
*   RESULT
*	Returns a pointer to the new block.  Pointer is of the type
*	struct IORequest.
*
*	0 indicates inability to allocate enough memory for the request block
*	or not enough signals available.
*
*   EXAMPLE
*	struct IORequest *myBlock;
*	if( (myBlock = CreateExtIO(myPort,sizeof(struct IOExtTD)) == NULL)
*		exit(NO_MEM_OR_SIGNALS);
*
*	example used to allocate space for IOExtTD (trackdisk driver
*	IO Request block for extended IO operations).
*
*   SEE ALSO
*	DeleteExtIO
*
***********************************************************************/

struct IORequest *CreateExtIO(ioReplyPort,size)
    struct MsgPort *ioReplyPort;
    LONG size;
{
    struct IORequest *ioReq;

    if (ioReplyPort == 0)
	return ((struct IORequest   *) 0);

    ioReq = (struct IORequest *)AllocMem (size, MEMF_CLEAR | MEMF_PUBLIC);

    if (ioReq == 0)
	return ((struct IORequest   *) 0);

    ioReq -> io_Message.mn_Node.ln_Type = NT_MESSAGE;
    ioReq -> io_Message.mn_Node.ln_Pri = 0;

    ioReq -> io_Message.mn_ReplyPort = ioReplyPort;

    return (ioReq);
}

/****** exec_support/DeleteExtIO **************************************
*
*   NAME
*	DeleteExtIO() - return memory allocated for extended IO request
*
*   SYNOPSIS
*	DeleteExtIO(ioReq,size);
*
*   FUNCTION
*	See summary line at NAME.  Also frees the signal bit which
*	had been allocated by the call to CreateExtIO.
*
*   INPUTS
*	A pointer to the IORequest block whose resources are to be freed.
*
*   RESULT
*	Frees the memory.  Returns (no error conditions shown)
*
*   EXAMPLE
*	struct IORequest *myBlock;
*	DeleteExtIO(myBlock,(sizeof(struct IOExtTD)));
*		
*	example shows that CreateExtIO had been used to create a trackdisk
*	(extended) IO Request block.
*
*   SEE ALSO
*	CreateExtIO
*
**************************************************************************/

DeleteExtIO(ioExt,size)
    struct IORequest *ioExt;
    LONG size;
{
    ioExt -> io_Message.mn_Node.ln_Type = 0xff;
    ioExt -> io_Device = (struct Device *) -1;
    ioExt -> io_Unit = (struct Unit *) -1;

    FreeMem (ioExt, size);
}
