[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

proposal for Ttime, etc. in MiNT



Have a look at the code and tell me better ways to implement the UTC based
time functions in MiNT. (It seems to work here on my machine.)

-- 
  jerry@stone
/* @(#)time.h, mint/jafmint
 */
struct timeval {
	long	tv_sec;		/* seconds since 1.1.1970 00:00 UTC */
	long	tv_usec;	/* microseconds since seconds */
};

struct timezone {
	int	tz_minuteswest;	/* local TZ is minutes west of GMT */
	int	tz_dsttime;	/* daylight savings time correction flag */
};

/* @(#)time.c, mint/jafmint
 * $Id$
 * by jerry
 *  - jerry@zedat.fu-berlin.de
 */

#include "mint.h"


/* global values, updated by 5ms timer */
long time_s;
long time_ms;
/* If anyone tells me how I could use some NON-fractional timer < 5ms
	(from Timer A which is unused I think) I'll do it, instead of
	5ms ticks. 
   HBLANK would be a better choice (28/64 usecs) if I could be
	sure it is not suspended during VBLANKS and and other interrupts
	and the values are really NON fractional.
 So you lot of experts please tell me what to do.
*/


/* moved here from timeout.c :
 used by filesystems for time/date stamps; updated once per second */
short timestamp, datestamp;


/* local values: */
static short minwest;
static short dst;
static long dstbeg, dstend; /* UTC notation of begin & end of DST 
 sorry, but we need this for Gemdos' DOSTIME calls */

static long do_dostime(void);

/* two functions and some values, moved from util.c  */

static int const
mth_start[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

#define SECS_PER_MIN    (60L)
#define SECS_PER_HOUR   (3600L)
#define SECS_PER_DAY    (86400L)
#define SECS_PER_YEAR   (31536000L)
#define SECS_PER_LEAPYEAR (SECS_PER_DAY + SECS_PER_YEAR)

static int
days_per_mth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };


/* convert a Unix time into a DOS time. The longword returned contains
   the time word first, then the date word.
*/

long ARGS_ON_STACK dostim(t)
	long t;
{
        unsigned long time, date;
	int tm_hour, tm_min, tm_sec;
	int tm_year, tm_mon, tm_mday;
	int i;

	if (t <= 0) return 0;

	tm_year = 70;
	while (t >= SECS_PER_YEAR) {
		if ((tm_year & 0x3) == 0) {
			if (t < SECS_PER_LEAPYEAR)
				break;
			t -= SECS_PER_LEAPYEAR;
		} else {
			t -= SECS_PER_YEAR;
		}
		tm_year++;
	}
	tm_mday = (int)(t/SECS_PER_DAY);
        days_per_mth[1] = (tm_year & 0x3) ? 28 : 29;
        for (i = 0; tm_mday >= days_per_mth[i]; i++)
                tm_mday -= days_per_mth[i];
        tm_mon = i+1;
	tm_mday++;
        t = t % SECS_PER_DAY;
        tm_hour = (int)(t/SECS_PER_HOUR);
        t = t % SECS_PER_HOUR;
        tm_min = (int)(t/SECS_PER_MIN);
        tm_sec = (int)(t % SECS_PER_MIN);

	if (tm_year < 80) {
		tm_year = 80;
		tm_mon = tm_mday = 1;
		tm_hour = tm_min = tm_sec = 0;
	}

	time = (tm_hour << 11) | (tm_min << 5) | (tm_sec >> 1);
	date = ((tm_year - 80) & 0x7f) << 9;
	date |= ((tm_mon) << 5) | (tm_mday);
	return (time << 16) | date;
}

/*
 * unixtim(time, date): convert a Dos style (time, date) pair into
 * a Unix time (seconds from midnight Jan 1., 1970)
 */

long ARGS_ON_STACK unixtim(time, date)
	unsigned time, date;
{
	int sec, min, hour;
	int mday, mon, year;
	long y, s;

	sec = (time & 31) << 1;
	min = (time >> 5) & 63;
	hour = (time >> 11) & 31;
	mday = date & 31;
	mon = ((date >> 5) & 15) - 1;
	year = 80 + ((date >> 9) & 255);

/* calculate tm_yday here */
	y = (mday - 1) + mth_start[mon] + /* leap year correction */
		( ( (year % 4) != 0 ) ? 0 : (mon > 1) );

	s = (sec) + (min * SECS_PER_MIN) + (hour * SECS_PER_HOUR) +
		(y * SECS_PER_DAY) + ((year - 70) * SECS_PER_YEAR) +
		((year - 69)/4) * SECS_PER_DAY;

	return s;
}


/* some internal funcs */

static int check_dst()
{
	if(time_s >= dstbeg && time_s < dstend)
		dst = 1;
	else
		dst = 0;
	return(dst);
}
/* 
 * init_time
 * called by main to init time variables
 * 
 * clockflag: which time runs the hardware clock (from mint/mint.init
 * 	or compiled in) and in which time is dstbeg, dstend (seconds since 1970)
 * 0 : UTC 1: localtime 2: localtime and DST (dstbeg is of course without DST)
 * best is to run UTC in hardware clock
 * 
 *
 * tz: (integer strings) <minwest>[,<dstbeg>,<dstend>]
 * 	(from mint/mint.init - there is a program, that generates the values)
 *
 * return 1 if bad TZ string
 */

static int expand_tz( short clock_flag, long clock_t, char *tz)
{
	char *p = tz; long clock_dstbeg, clock_dstend;
	if(!p || !*p)
		return(0);
	minwest = atol(p);	
	while(*p && *p != ',')
		p++;
	if(*p++ != ',')
		return(0);
	clock_dstbeg = atol(p);
	while(*p && *p != ',')
		p++;
	if(*p++ != ',')
		return(1);
	clock_dstend = atol(p);

	/* init check_dst */
	if(clock_t >= clock_dstbeg) {
		if(clock_t < clock_dstend)
			dst = 1;
	}
	if(clock_flag) {
		dstbeg = minwest * 60 + clock_dstbeg;
		dstend = minwest * 60 + clock_dstend;
		if( (clock_flag == 2) && dst )
			dstend -= 3600;
	}

	return(0);	
}

static int clock_vals[6]; /* Y,M,d,h,m,s */

#if 0
static u_char ikbd_timeofday[7];
#endif

static void ikdb_gettime()
{
#if 0
	char cs[2];void *oldclock; KBDVECS *kbdv;
	*cs = 0x1c;
	/* get the hardware time (IKBD) */
	kbdv = (KBDVECS *)Kbdvbase(); /* isn't that well known in the kernel ? */
	oldclock = kbdv->clockvec;
	kbdv->clockvec = ikbd_clock;
	while(kbdv->drvstat);
	Ikbdws(0, cs);
	while(!ikbd_timeofday[0]);	/* wait	*/
	kbdv->clockvec = oldclock;

	ikbd_timeofday -> clock_vals
#endif
}
#if 0
/* better immediately bcd to short clock_vals[] */
static void ikbd_clock( char *timeofday)
{
	u_char *p = ikbd_timeofday;
	*p++ = *timeofday++;
	*p++ = *timeofday++;
	*p++ = *timeofday++;
	*p++ = *timeofday++;
	*p++ = *timeofday++;
	*p++ = *timeofday++;
	*p = *timeofday;
}
#endif

static char savalarm;
static int testmega0()
{
	char *pc = (char *)0xfffffc3b, c;
	char *pca = (char *)0xfffffc2f;
	c = *pc & 0x0f;
	if(c & 8) {	/* running	*/
		c |= 1;
		*pc = c;
			savalarm = *pca;
			*pca = 10;
		return(1);
	}
	return(0);
}

static int testmega1()
{
	char *pc = (char *)0xfffffc3b, c;
	char *pca = (char *)0xfffffc2f;
	c = *pc & 0x0f;
	if(! (c & 1)) {
		*pc = c|1;
	}
	
	c = *pca & 0x0f;
	if(c != 10)
		return(0);
	*pca = savalarm;
		/* switch bank 0 */
	c = *pc & 0x0c;
	*pc = c;
	return(1);
}

static void mega_gettime()
{
	short *pi = (short *)0xfffffc38;
	clock_vals[0] = (*pi-- & 0x0f) * 10;	/* year */
	clock_vals[0] += (*pi-- & 0x0f);
	clock_vals[1] = (*pi-- & 0x0f) * 10;
	clock_vals[1] += (*pi-- & 0x0f);
	clock_vals[2] = (*pi-- & 0x0f) * 10;
	clock_vals[2] += (*pi-- & 0x0f);
	pi--;	/* skip day of week */
	clock_vals[3] = (*pi-- & 0x0f) * 10;
	clock_vals[3] += (*pi-- & 0x0f);
	clock_vals[4] = (*pi-- & 0x0f) * 10;
	clock_vals[4] += (*pi-- & 0x0f);
	clock_vals[5] = (*pi-- & 0x0f) * 10;	/* seconds */
	clock_vals[5] += (*pi-- & 0x0f);
}

int	init_time( short clockflag, char *tz )
{
	int r_val = 0;
#if 0	
	if(!testmega0() || !testmega1())
		ikdb_gettime();
	else
		mega_gettime();
#else
	if(!testmega0()) {
		/* clock is not running */
		FORCE("hard ware clock is not running:  Set time!");
	} else {
		testmega1();
		mega_gettime();
	}
#endif

	time_ms = 0;
	time_s = 0;
	/* calc UTC: secs + mins + hours + days -1(not today) + month*days + 
			(leap year? february passed ? 1 day : 0 : 0 ) + years + ... */
	time_s = clock_vals[5] + clock_vals[4] * SECS_PER_MIN + 
				clock_vals[3] * SECS_PER_HOUR +
				(clock_vals[2] -1 + mth_start[clock_vals[1]-1]) * SECS_PER_DAY +
				(clock_vals[0]%4 ? 0 : (clock_vals[1] > 2 ? SECS_PER_DAY : 0)) +
				(clock_vals[0] + 10)* SECS_PER_YEAR + 
			3 * SECS_PER_DAY + ((clock_vals[0]-1)/4) * SECS_PER_DAY;
			/* ... leap year days between 1970 and 1980, 1980 and last year */

	r_val = expand_tz(clockflag, time_s, tz);

	if(clockflag) {
		time_s += minwest * 60;
		if( (clockflag == 2) && dst )
			time_s -= 3600;
	}
	update_time();	/* set datestamp, timestamp */
	return(r_val);	
}	/* init_time	*/




/* 
 * update_time
 * 
 * update datestamp and timestamp (called by checkalarms(), timeout.c 
 *		once per sec things)
 * maybe count secs and check every 5/10 minutes hardware clock/time_s
 * difference
 */

void update_time()
{
	unsigned long t;
	t = dostim(time_s);	/* UTC ?? or local time : = do_dostime(); */
	timestamp = (t>>16) & 0xffff; /* Tgettime(); */
	datestamp = t & 0xffff;		/* Tgetdate(); */

}	/* update_time	*/




/* 
 * OS calls get time:
 *
 * time_t Ttime(time_t *) may be NULL
 * Tgettimeofday(struct timeval *, struct timezone *) each may be NULL
 * these dostime calls return local time including dst:
 * Tgetdate( void )
 * Tgettime( void )
 */


/* 
 * t_time
 * 
 * 
 */

long ARGS_ON_STACK t_time( long *tp )
{
	if(tp)
		*tp = time_s;

	return(time_s);

}	/* t_time	*/




/* 
 * t_gettimeofday
 * 
 * 
 */

long ARGS_ON_STACK t_gettimeofday(struct timeval *tvp, struct timezone *tzp)
{
	if(tzp) {
		tzp->tz_minuteswest = minwest;	/* local TZ is minutes west of UTC */
		tzp->tz_dsttime = check_dst();	/* 1 : add 60 minutes for dst */
	}
	if(tvp) {
		tvp->tv_sec = time_s;
		tvp->tv_usec = 1000 * time_ms;
	}

	return(0);
}	/* t_gettimeofday	*/



/* 
 * old gemdos calls
 * 
 * It is no longer a bug in dostim() to ignore TZ/dst cause we
 * add it before, and dostim may be used for generating UTC dostime
 * values, too (e.g for filesystems).
 */
static long do_dostime(void)
{
	long local_t;
	
	check_dst();
	local_t = time_s;
	local_t -= minwest * 60;
	if(dst)
		local_t += 3600;
	return(dostim(local_t));
}

long ARGS_ON_STACK t_getdate (void)
{
	long t;
	t = do_dostime();
	return(t & 0xffff);
}


long ARGS_ON_STACK t_gettime (void)
{
	long t;
	t = do_dostime();
	return((t>>16) & 0xffff);
}


/* 
 * OS calls set time:
 * change time_s and time_ms variables (maybe in future the hardware clock too)
 *		only superuser
 * Tstime(time_t)
 * Tsettimeofday( const ...)
 * Tsetime(short dostime) (local, dst including time)
 * Tsetdate(short dosdate) (local time)
 */

long ARGS_ON_STACK t_stime( long *tp )
{
	return(-1);
}
long ARGS_ON_STACK t_settimeofday(struct timeval *tvp, struct timezone *tzp)
{
	return(-1);
}


/* 
 * and comming soon,  for 5ms ticks and real UTC differ
 * Tadjtime()
 * only superuser
 */
long ARGS_ON_STACK t_adjtime( const struct timeval *delta, 
	struct timeval *olddelta)
{
	return(-1);
}

/* that's it */