mirror of
https://frontier.innolan.net/rainlance/amiga-ntimed.git
synced 2025-11-21 00:19:44 +00:00
344 lines
8.2 KiB
C
344 lines
8.2 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/io.h>
|
|
#include <exec/types.h>
|
|
#include <exec/memory.h>
|
|
#include <devices/timer.h>
|
|
#include <proto/dos.h>
|
|
#include <proto/exec.h>
|
|
#include <proto/timer.h>
|
|
#include <proto/locale.h>
|
|
#include <proto/battclock.h>
|
|
#include <clib/alib_protos.h>
|
|
#include <clib/exec_protos.h>
|
|
#include <clib/utility_protos.h>
|
|
#include <clib/battclock_protos.h>
|
|
|
|
#ifdef Debug
|
|
#undef Debug
|
|
#endif
|
|
|
|
#include "ntimed.h"
|
|
#include "ntimed_platform.h"
|
|
|
|
int amiga_get_time(struct timeval *tv);
|
|
int amiga_set_time(struct timeval *tv);
|
|
struct timerequest* create_timer();
|
|
void delete_timer(struct timerequest*);
|
|
static 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
|
|
|
|
struct timeval sync_time;
|
|
int validtime = 0;
|
|
int limited_sync = 0;
|
|
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;
|
|
|
|
if (limited_sync) {
|
|
adjust_timeval(&sync_time, -dur);
|
|
if ((LONG)sync_time.tv_secs < 0L) {
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
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(TIMER_NAME, 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);
|
|
validtime = 1;
|
|
return 0;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
void amiga_save_time(void)
|
|
{
|
|
struct timeval tv;
|
|
long tzoffset;
|
|
|
|
if(!validtime)
|
|
return;
|
|
|
|
ResetBattClock();
|
|
amiga_get_time(&tv);
|
|
|
|
tzoffset = (long)round(utcoffset * 60.0 * 60.0);
|
|
WriteBattClock((long)tv.tv_secs + tzoffset);
|
|
}
|
|
|
|
void amiga_sync_time(int seconds)
|
|
{
|
|
limited_sync = 1;
|
|
sync_time.tv_secs = seconds;
|
|
sync_time.tv_micro = 0;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
static 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;
|
|
}
|