mirror of
https://frontier.innolan.net/rainlance/amiga-ntimed.git
synced 2026-05-03 05:51:37 +00:00
Initial commit
This commit is contained in:
283
time_amiga.c
Normal file
283
time_amiga.c
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <string.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 <devices/timer.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
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
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)
|
||||
{
|
||||
double d;
|
||||
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)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
adjust_timeval(&tv, dur);
|
||||
|
||||
struct timerequest *request = create_timer();
|
||||
if (request == NULL)
|
||||
return 1;
|
||||
|
||||
request->tr_node.io_Command = TR_ADDREQUEST;
|
||||
request->tr_time = tv;
|
||||
|
||||
DoIO((struct IORequest*)request);
|
||||
|
||||
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()
|
||||
{
|
||||
struct MsgPort *port = CreatePort(0, 0);
|
||||
if (port == NULL)
|
||||
return NULL;
|
||||
|
||||
struct timerequest *request = (struct timerequest*)CreateExtIO(port, sizeof(struct timerequest));
|
||||
if (request == NULL)
|
||||
{
|
||||
DeletePort(port);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LONG error = OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest*)request, 0L);
|
||||
if (error != 0)
|
||||
{
|
||||
delete_timer(request);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
void delete_timer(struct timerequest *request)
|
||||
{
|
||||
if (request == NULL)
|
||||
return;
|
||||
|
||||
struct MsgPort *port = request->tr_node.io_Message.mn_ReplyPort;
|
||||
|
||||
if (port != 0)
|
||||
DeletePort(port);
|
||||
|
||||
CloseDevice((struct IORequest*)request);
|
||||
DeleteExtIO((struct IORequest*)request);
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
// 2922 is the number of days between 1.1.1970 and 1.1.1978 (2 leap years and 6 normal)
|
||||
const long amigaoffset = 2922 * 24 * 60 * 60; // 252460800
|
||||
const long cetoffset = 60 * 60;
|
||||
|
||||
int amiga_get_time(struct timeval *tv)
|
||||
{
|
||||
struct timerequest *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 = tv->tv_secs + (amigaoffset - cetoffset);
|
||||
|
||||
delete_timer(request);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amiga_set_time(struct timeval *tv)
|
||||
{
|
||||
struct timerequest *request = create_timer();
|
||||
if (request == NULL)
|
||||
return 1;
|
||||
|
||||
request->tr_node.io_Command = TR_SETSYSTIME;
|
||||
request->tr_time = *tv;
|
||||
request->tr_time.tv_secs -= (amigaoffset - cetoffset);
|
||||
|
||||
DoIO((struct IORequest*)request);
|
||||
|
||||
delete_timer(request);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
void adjust_timeval(struct timeval *tv, double offset)
|
||||
{
|
||||
double d = floor(offset);
|
||||
offset -= d;
|
||||
|
||||
long sec = tv->tv_secs + (long)d;
|
||||
long micro = tv->tv_micro + (long)floor(offset * 1e6);
|
||||
|
||||
if (micro < 0) {
|
||||
sec -= 1;
|
||||
micro += 1000000;
|
||||
} else if (micro >= 1000000) {
|
||||
sec += 1;
|
||||
micro -= 1000000;
|
||||
}
|
||||
|
||||
tv->tv_secs = sec;
|
||||
tv->tv_micro = micro;
|
||||
}
|
||||
Reference in New Issue
Block a user