/* time_routines.c
   AmigaDos supplies us with a Delay() function we can use in our
   programs which takes an argument in 1/50ths or 1/60ths of a second.
   This is too coarse grained, and is dependent on which country the
   Amiga is in.  A better way to wait a specified amount of time
   is using the timer.device.
   Copyright © 1993 Robert Poole
*/

#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/timer.h>

#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>

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

#include "time_routines.h"

struct timerequest *TimerIO = NULL;
struct timerequest *TimeQueue[NUM_TIME_SLOTS];
struct MsgPort *TimerPort = NULL;

/* int setup_timer(void)
   sets up all the necessary stuff for the timer.device
   returns TRUE if successful, FALSE otherwise */

int setup_timer(void)
{
  int i, j;

  /* message port for communicating with timer device */
  TimerPort = CreateMsgPort();
  if (TimerPort == NULL) {
    return(FALSE);
  }
  /* our primary IO structure */
  TimerIO = (struct timerequest *) CreateExtIO(TimerPort,
					       sizeof(struct timerequest));
  if (TimerIO == NULL) {
    DeleteMsgPort(TimerPort);
    return(FALSE);
  }

  /* open the timer device and initialize it */
  if (OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *) TimerIO,
		 0L)) {
    DeleteMsgPort(TimerPort);
    DeleteExtIO((struct IORequest *) TimerIO);
    return(FALSE);
  }

  /* set up a primitive queue structure so we can make multiple requests */
  for (i = 0; i < NUM_TIME_SLOTS; i++) {
    TimeQueue[i] = (struct timerequest *) AllocMem(sizeof(struct timerequest),
						   MEMF_PUBLIC | MEMF_CLEAR);
    if (TimeQueue[i] == NULL) {
      if (i > 0) {
	for (j = 0; j < i; j++) {
	  FreeMem((void *) TimeQueue[j], sizeof(struct timerequest));
	}
      }
      CloseDevice((struct IORequest *) TimerIO);
      DeleteMsgPort(TimerPort);
      DeleteExtIO((struct IORequest *) TimerIO);
      return(FALSE);
    }

    *TimeQueue[i] = *TimerIO;
  }

  /* success! */
  return(TRUE);
}
/* end: setup_timer() */

/* void dispose_timer_resources(void)
   deallocate any and all allocated timer resources */

void dispose_timer_resources(void)
{
  int i;

  CloseDevice((struct IORequest *) TimerIO);
  DeleteMsgPort(TimerPort);
  DeleteExtIO((struct IORequest *) TimerIO);
  for (i = 0; i < NUM_TIME_SLOTS; i++) {
    FreeMem((void *) TimeQueue[i], sizeof(struct timerequest));
  }
}
/* end: dispose_timer_resources() */

/* void synchronous_wait(unsigned long secs, unsigned long microsecs)
   wait for specified amount of time and then return */

void synchronous_wait(unsigned long secs, unsigned long microsecs)
{
  struct timeval tv_temp;

  tv_temp.tv_secs = secs;
  tv_temp.tv_micro = microsecs;

  TimerIO->tr_node.io_Command = TR_ADDREQUEST;
  TimerIO->tr_time = tv_temp;

  DoIO((struct IORequest *) TimerIO);
}
/* end: synchronous_wait() */

/* void asynchronous_wait(unsigned long secs, unsigned long microsecs)
   same as synchronous_wait(), except that it returns immediately
   the program can then go about its business, then call
   wait_asynch_finish() to wait for the timer request to finish */

void asynchronous_wait(unsigned long secs, unsigned long microsecs)
{
  struct timeval tv_temp;

  tv_temp.tv_secs = secs;
  tv_temp.tv_micro = microsecs;

  TimerIO->tr_node.io_Command = TR_ADDREQUEST;
  TimerIO->tr_time = tv_temp;

  SendIO((struct IORequest *) TimerIO);
}
/* end: asynchronous_wait() */

/* void wait_asynch_finish(void)
   wait for an asynchronously started timer request to finish */

void wait_asynch_finish(void)
{
  WaitIO((struct IORequest *) TimerIO);
}
/* end: wait_asynch_finish() */

/* void queue_asynch_wait(int slot, unsigned long secs, unsigned long microsecs)
   queue a request to wait in the given slot */

void queue_asynch_wait(int slot, unsigned long secs, unsigned long microsecs)
{
  /* check first to make sure we're in a valid slot range */
  if (slot > NUM_TIME_SLOTS) {
    return;
  }
  /* send off a request */
  TimeQueue[slot]->tr_node.io_Command = TR_ADDREQUEST;
  TimeQueue[slot]->tr_time.tv_secs = secs;
  TimeQueue[slot]->tr_time.tv_micro = microsecs;
  SendIO((struct IORequest *) TimeQueue[slot]);
}
/* end: queue_asynch_wait() */

/* int queue_next_finish(void)
   wait for the next event in the time queue to end */

int queue_next_finish(void)
{
  int i;
  struct Message *timer_msg;

  WaitPort(TimerPort);
  timer_msg = GetMsg(TimerPort);
  /* match the next event to complete */
  for (i = 0; i < NUM_TIME_SLOTS; i++) {
    if (timer_msg == (struct Message *) TimeQueue[i]) {
      return(i);
    }
  }
}
/* end: queue_next_finish() */
