/*
 *    UnixClock - make the amiga hardware clock behave like a UNIX clock
 *
 *    © Copyright 1995 by Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be)
 *    © Copyright 1997 by Gunther Nikl (gnikl@informatik.uni-rostock.de)
 *
 *    -----------------------------------------------------------------------------
 *
 *    This file is subject to the terms and conditions of the GNU General Public
 *    License.
 */

/*
** includes
*/

#include <exec/types.h>

#ifndef REG
#define REG(x) asm(#x)
#endif

#include <dos/dos.h>
#include <dos/dosextens.h>
#include <devices/timer.h>
#include <libraries/locale.h>
#include <resources/battclock.h>
#include <proto/battclock.h>
#include <proto/locale.h>
#include <proto/exec.h>

/*
** nifty structure
*/

struct BattInfo {
  ULONG (*OldReadBattClock)();
  VOID  (*OldWriteBattClock)(ULONG REG(d0));
  ULONG GMTOffset;
} bi;

/*
** skip any const data and functions *in front of* our Main !
*/

REG(jra _Main);

/*
** patches
*/

ULONG NewReadBattClock()
{
  return (*bi.OldReadBattClock)()-bi.GMTOffset;
}

VOID NewWriteBattClock(ULONG time REG(d0))
{
  (*bi.OldWriteBattClock)(time+bi.GMTOffset);
}

/*
** support
*/

STATIC VOID SetClock(struct ExecBase *SysBase REG(a6), struct timerequest *tr REG(a1), ULONG time REG(d0))
{
  tr->tr_node.io_Command = TR_SETSYSTIME;
  tr->tr_node.io_Flags   = IOF_QUICK;
  tr->tr_time.tv_micro   = 0;
  tr->tr_time.tv_secs    = time;
  DoIO(&tr->tr_node);
}

/*
** version
*/

const char version[] = "$VER: UnixClock 1.1 (13.2.97)";

/*
** main
*/

#define PORTNAME "UC.rendezvous"

LONG Main()
{
  struct LocaleBase *LocaleBase;
  struct Node *BattClockBase;
  struct ExecBase *SysBase;
  struct timerequest *treq;
  struct MsgPort *tport,*mp;
  struct Message *wbmsg;
  struct Process *pr;

  SysBase = *(struct ExecBase **)4L; pr = (struct Process *)FindTask(NULL);

  wbmsg = NULL;

  if (!pr->pr_CLI) {
    WaitPort(&pr->pr_MsgPort); wbmsg = GetMsg(&pr->pr_MsgPort);
  }

  Forbid();
  if ((mp=FindPort(PORTNAME)) == NULL)
    if ((tport=CreateMsgPort()) != NULL) {
      tport->mp_Node.ln_Name = PORTNAME; AddPort(tport);
    }
  Permit();

  if (!mp && tport) {
    if ((BattClockBase=OpenResource(BATTCLOCKNAME)) != NULL) {
      if ((LocaleBase=(struct LocaleBase *)OpenLibrary("locale.library",38L)) != NULL) {
        bi.GMTOffset = 60 * (OpenLocale(NULL))->loc_GMTOffset;
        CloseLibrary((struct Library *)LocaleBase);
        if ((treq=CreateIORequest(tport, sizeof(*treq))) != NULL) {
          if (!OpenDevice(TIMERNAME, UNIT_VBLANK, &treq->tr_node, NULL)) {
            Forbid();
            bi.OldReadBattClock  = SetFunction((struct Library *)BattClockBase, -12L, (APTR)NewReadBattClock);
            bi.OldWriteBattClock = SetFunction((struct Library *)BattClockBase, -18L, (APTR)NewWriteBattClock);
            Permit();
            SetClock(SysBase,treq,ReadBattClock());
            SetSignal(NULL, SIGBREAKF_CTRL_C); Wait(SIGBREAKF_CTRL_C);
            Forbid();
            (VOID)SetFunction((struct Library *)BattClockBase, -18L, (APTR)bi.OldWriteBattClock);
            (VOID)SetFunction((struct Library *)BattClockBase, -12L, (APTR)bi.OldReadBattClock);
            Permit();
            SetClock(SysBase,treq,ReadBattClock());
            CloseDevice(&treq->tr_node);
          }
          DeleteIORequest(treq);
        }
      }
    }
    RemPort(tport); DeleteMsgPort(tport);
  }

  if (wbmsg != NULL) {
    Forbid(); ReplyMsg(wbmsg);
  }
    
  return 0;
}
