1
0
mirror of https://frontier.innolan.net/rainlance/amiga-ntimed.git synced 2025-11-21 14:03:42 +00:00
Files
amiga-ntimed/time_amiga.c
2015-03-16 21:25:54 +01:00

316 lines
7.5 KiB
C

/*
* Copyright (c) 2015 Carsten Larsen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <clib/alib_protos.h>
#include <proto/dos.h>
#include <clib/battclock_protos.h>
#include <devices/timer.h>
#include <signal.h>
#ifdef Debug
#undef Debug
#endif
#include "atimed.h"
#include "ntimed.h"
int amiga_get_time(struct timeval *tv);
int amiga_set_time(struct timeval *tv);
struct timerequest* create_timer();
void delete_timer(struct timerequest*);
void adjust_timeval(struct timeval *tv, double offset);
// Ntimed internals
static double adj_offset = 0;
static double adj_duration = 0;
static double adj_freq = 0;
static uintptr_t ticker;
static struct todolist *kt_tdl;
#ifndef AMIGA
struct timeval {
ULONG tv_secs;
ULONG tv_micro;
};
#endif
float utcoffset = 0.0;
/**********************************************************************/
static void
amiga_setfreq(struct ocx *ocx, double frequency)
{
//assert(isfinite(frequency));
// frequency offset (scaled ppm)
// long int freq = (long)floor(frequency * (65536 * 1e6));
Put(ocx, OCX_TRACE, "KERNPLL %.6e\n", frequency);
}
static enum todo_e __match_proto__(todo_f)
amiga_ticker(struct ocx *ocx, struct todolist *tdl, void *priv)
{
(void)ocx;
AN(tdl);
AZ(priv);
amiga_setfreq(ocx, adj_freq);
ticker = 0;
return (TODO_OK);
}
static void __match_proto__(tb_adjust_f)
amiga_adjust(struct ocx *ocx, double offset, double duration, double frequency)
{
double freq;
(void)ocx;
assert(duration >= 0.0);
if (ticker)
TODO_Cancel(kt_tdl, &ticker);
adj_offset = offset;
adj_duration = floor(duration);
if (adj_offset > 0.0 && adj_duration == 0.0)
adj_duration = 1.0;
adj_freq = frequency;
freq = adj_freq;
if (adj_duration > 0.0)
freq += adj_offset / adj_duration;
amiga_setfreq(ocx, freq);
if (adj_duration > 0.0)
ticker = TODO_ScheduleRel(kt_tdl, amiga_ticker, NULL,
adj_duration, 0.0, "KT_TICK");
}
/**********************************************************************/
static void __match_proto__(tb_step_f)
amiga_step(struct ocx *ocx, double offset)
{
struct timeval tv;
Put(ocx, OCX_TRACE, "KERNTIME_STEP %.3e\n", offset);
AZ(amiga_get_time(&tv));
adjust_timeval(&tv, offset);
AZ(amiga_set_time(&tv));
TB_generation++;
}
/**********************************************************************/
static struct timestamp * __match_proto__(tb_now_f)
amiga_now(struct timestamp *storage)
{
struct timeval tv;
AZ(amiga_get_time(&tv));
return (TS_Nanosec(storage, tv.tv_secs, tv.tv_micro * 1000LL));
}
/**********************************************************************/
static int __match_proto__(tb_sleep_f)
amiga_sleep(double dur)
{
ULONG sigs, timersig;
struct timerequest *request;
struct timeval tv;
tv.tv_secs = 0;
tv.tv_micro = 0;
adjust_timeval(&tv, dur);
request = create_timer();
if (request == NULL)
return 1;
request->tr_node.io_Command = TR_ADDREQUEST;
request->tr_time = tv;
timersig = (1L << request->tr_node.io_Message.mn_ReplyPort->mp_SigBit);
SendIO((struct IORequest*)request);
sigs = Wait(SIGBREAKF_CTRL_C | timersig);
if (sigs & SIGBREAKF_CTRL_C) {
AbortIO((struct IORequest*)request);
WaitIO((struct IORequest*)request);
delete_timer(request);
exit(1);
}
delete_timer(request);
return 0;
}
/**********************************************************************/
void Time_Amiga(struct todolist *tdl)
{
AN(tdl);
TB_Step = amiga_step;
TB_Adjust = amiga_adjust;
TB_Sleep = amiga_sleep;
TB_Now = amiga_now;
kt_tdl = tdl;
}
void Time_Amiga_Passive(void)
{
TB_Sleep = amiga_sleep;
TB_Now = amiga_now;
}
/**********************************************************************/
struct timerequest* create_timer()
{
LONG error;
struct timerequest *request;
struct MsgPort *port = CreatePort(0, 0);
if (port == NULL)
return NULL;
request = (struct timerequest*)CreateExtIO(port, sizeof(struct timerequest));
if (request == NULL)
{
DeletePort(port);
return NULL;
}
error = OpenDevice(ATIMER, UNIT_MICROHZ, (struct IORequest*)request, 0L);
if (error != 0)
{
delete_timer(request);
return NULL;
}
return request;
}
void delete_timer(struct timerequest *request)
{
struct MsgPort *port;
if (request == NULL)
return;
port = request->tr_node.io_Message.mn_ReplyPort;
if (port != 0)
DeletePort(port);
CloseDevice((struct IORequest*)request);
DeleteExtIO((struct IORequest*)request);
}
/**********************************************************************/
int amiga_get_time(struct timeval *tv)
{
struct timerequest *request;
long tzoffset = (long)round(utcoffset * 60.0 * 60.0);
request = create_timer();
if (request == NULL)
return 1;
request->tr_node.io_Command = TR_GETSYSTIME;
DoIO((struct IORequest*)request);
*tv = request->tr_time;
tv->tv_secs = (long)tv->tv_secs - tzoffset;
delete_timer(request);
return 0;
}
int amiga_set_time(struct timeval *tv)
{
struct timerequest *request;
long tzoffset = (long)round(utcoffset * 60.0 * 60.0);
request = create_timer();
if (request == NULL)
return 1;
request->tr_node.io_Command = TR_SETSYSTIME;
request->tr_time = *tv;
request->tr_time.tv_secs += tzoffset;
DoIO((struct IORequest*)request);
delete_timer(request);
return 0;
}
void amiga_save_time(void)
{
#ifndef AROS
struct timeval tv;
amiga_get_time(&tv);
WriteBattClock(tv.tv_secs);
#endif
}
/**********************************************************************/
void adjust_timeval(struct timeval *tv, double offset)
{
double d1, d2;
long sec, micro;
d1 = floor(offset);
d2 = offset - d1;
sec = tv->tv_secs + (long)d1;
micro = tv->tv_micro + (long)floor(d2 * 1e6);
if (micro < 0) {
sec -= 1;
micro += 1000000;
} else if (micro >= 1000000) {
sec += 1;
micro -= 1000000;
}
tv->tv_secs = sec;
tv->tv_micro = micro;
}