/*
 * This file has been modified as part of the MH-MiNT project. See
 * the file Changes.MH for details and dates.
 */

/*
Copyright 1990,1991,1992 Eric R. Smith.
Copyright 1992,1993 Atari Corporation.
All rights reserved.
*/

/*
 * misc. utility routines
 */

#include "mint.h"

/*
 * given an address, find the corresponding memory region in this program's
 * memory map
 */

MEMREGION *
addr2mem(a)
	virtaddr a;
{
	int i;

	for (i = 0; i < curproc->num_reg; i++) {
		if (a == curproc->addr[i])
			return curproc->mem[i];
	}
	return 0;
}

/*
 * given a pid, return the corresponding process
 */

PROC *
pid2proc(pid)
	int pid;
{
	PROC *p;

	for (p = proclist; p; p = p->gl_next) {
		if (p->pid == pid)
			return p;
	}
	return 0;
}

/*
 * return a new pid
 */

static int _maxpid = 2;
int
newpid()
{
	int i;
#ifndef NDEBUG
	int j = 0;
#endif

	do {
		i = _maxpid++;
		if (_maxpid >= 1000) _maxpid = 2;
		assert(j++ < 1000);
	} while (pid2proc(i));

	return i;
}

/*
 * set up to run the init program as PID 1
 */
int
set_pid_1()
{
	if (pid2proc(1)) /* should never happen, only called once */
		return -1;
	_maxpid = 1;
	return 0;
}

/*
 * zero out a block of memory, quickly; the block must be word-aligned,
 * and should be long-aligned for speed reasons
 */

void
zero(place, size)
	char *place;
	long size;
{
	long cruft;
	long blocksize;

	cruft = size % 256;	/* quickzero does 256 byte blocks */
	blocksize = size/256;	/* divide by 256 */
	if (blocksize > 0) {
		quickzero(place, blocksize);
		place += (blocksize*256);
	}
	while (cruft > 0) {
		*place++ = 0;
		cruft--;
	}
}

#ifdef JUNK_MEM
void
fillwjunk(place, size)
	long *place;
	long size;
{
	while (size > 0) {
		*place++ = size;
		size -= 4;
	}
}
#endif

/*
 * kernel memory allocation routines
 */

#define KERMEM_THRESHOLD (QUANTUM-8)
#if 0
#define KERMEM_SIZE QUANTUM
#else
#define KERMEM_SIZE ((KERMEM_THRESHOLD+8)*2)
#endif
#define KMAGIC ((MEMREGION *)0x87654321L)
#define NKMAGIC 0x19870425L

void * ARGS_ON_STACK 
kmalloc(size)
	long size;
{
	MEMREGION *m;
	MEMREGION **p;
	long *lp;

	/*
	 * increase size by two pointers' worth: the first contains
	 * a pointer to the region descriptor for this block, and the
	 * second contains KMAGIC.  If the block came from nalloc,
	 * then they both contain NKMAGIC.
	 */
	size += sizeof(m) + sizeof(m);
/*
 * for small requests, we use nalloc first
 */
tryagain:
	if (size < KERMEM_THRESHOLD) {
	    lp = nalloc(size);
	    if (lp) {
		*lp++ = NKMAGIC;
		*lp++ = NKMAGIC;
		TRACELOW(("kmalloc(%lx) -> (nalloc) %lx",size,lp));
		return lp;
	    }
	    else {
		DEBUG(("kmalloc(%lx): nalloc is out of memory",size));

	/* If this is commented out, then we fall through to try_getregion */
		if (0 == (m = get_region(alt, KERMEM_SIZE, PROT_S))) {
		    if (0 == (m = get_region(core, KERMEM_SIZE, PROT_S))) {
			DEBUG(("No memory for another arena"));
			goto try_getregion;
		    }
		}
		lp = (long *)m->loc;
		*lp++ = (long)KMAGIC;
		*lp++ = (long)m;
		nalloc_arena_add((void *)lp,KERMEM_SIZE - 2*SIZEOF(long));
		goto tryagain;
	    }
	}

try_getregion:
	m = get_region(alt, size, PROT_S);

	if (!m) m = get_region(core, size, PROT_S);

	if (m) {
		p = (MEMREGION **)m->loc;
		*p++ = KMAGIC;
		*p++ = m;
		TRACELOW(("kmalloc(%lx) -> (get_region) %lx",size,p));
		return (void *)p;
	}
	else {
		TRACELOW(("kmalloc(%lx) -> (fail)",size));
#if 0
		/* this is a serious offense; I want to hear about it */
		/* maybe Allan wanted to hear about it, but ordinary users
		 * won't! -- ERS
		 */
		NALLOC_DUMP();
		BIG_MEM_DUMP(0,0);
#endif
		return 0;
	}
}

/* allocate from ST memory only */

void *
kcore(size)
	long size;
{
	MEMREGION *m;
	MEMREGION **p;

	size += sizeof(m) + sizeof (m);
	m = get_region(core, size, PROT_S);

	if (m) {
		p = (MEMREGION **)m->loc;
		*p++ = KMAGIC;
		*p++ = m;
		return (void *)p;
	}
	else {
		return 0;
	}
}

void ARGS_ON_STACK 
kfree(place)
	void *place;
{
	MEMREGION **p;
	MEMREGION *m;

	TRACELOW(("kfree(%lx)",place));

	if (!place) return;
	p = place;
	p -= 2;
	if (*p == (MEMREGION *)NKMAGIC) {
	    nfree(p);
	    return;
	}
	else if (*p++ != KMAGIC) {
		FATAL("kfree: memory not allocated by kmalloc");
	}
	m = *p;
	if (--m->links != 0) {
		FATAL("kfree: block has %d links", m->links);
	}
	free_region(m);
}

/*
 * "user" memory allocation routines; the kernel can use these to
 * allocate/free memory that will be attached in some way to a process
 * (and freed automatically when the process exits)
 */
void * ARGS_ON_STACK 
umalloc(size)
	long size;
{
	return (void *)m_xalloc(size, 3);
}

void ARGS_ON_STACK 
ufree(block)
	void *block;
{
	(void)m_free((virtaddr)block);
}

/*
 * convert a time in milliseconds to a GEMDOS style date/time
 * timeptr[0] gets the time, timeptr[1] the date.
 * BUG/FEATURE: in the conversion, it is assumed that all months have
 * 30 days and all years have 360 days.
 */

void ARGS_ON_STACK 
ms_time(ms, timeptr)
	ulong ms;
	short *timeptr;
{
	ulong secs;
	short tsec, tmin, thour;
	short tday, tmonth, tyear;

	secs = ms / 1000;
	tsec = secs % 60;
	secs /= 60;		/* secs now contains # of minutes */
	tmin = secs % 60;
	secs /= 60;		/* secs now contains # of hours */
	thour = secs % 24;
	secs /= 24;		/* secs now contains # of days */
	tday = secs % 30;
	secs /= 30;
	tmonth = secs % 12;
	tyear = secs / 12;
	*timeptr++ = (thour << 11) | (tmin << 5) | (tsec >> 1);
	*timeptr = (tyear << 9) | ((tmonth + 1) << 5) | (tday+1);
}

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

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

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 * 60L) + (hour * 3600L) +
		(y * 86400L) + ((year - 70) * 31536000L) +
		((year - 69)/4) * 86400L;

	return s;
}

/* convert a Unix time into a DOS time. The longword returned contains
   the time word first, then the date word.
   BUG: we completely ignore any time zone information.
*/
#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 };

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;
}

/*
 * Case insensitive string comparison. note that this only returns
 * 0 (match) or nonzero (no match), and that the returned value
 * is not a reliable indicator of any "order".
 */

int ARGS_ON_STACK 
strnicmp(str1, str2, len)
	register const char *str1, *str2;
	register int len;
{
	register char c1, c2;

	do {
		c1 = *str1++; if (isupper(c1)) c1 = tolower(c1);
		c2 = *str2++; if (isupper(c2)) c2 = tolower(c2);
	} while (--len >= 0 && c1 && c1 == c2);

	if (len < 0 || c1 == c2)
		return 0;
	return c1 - c2;
}

int ARGS_ON_STACK 
stricmp(str1, str2)
	const char *str1, *str2;
{
	return strnicmp(str1, str2, 0x7fff);
}


/*
 * string utilities: strlwr() converts a string to lower case, strupr()
 * converts it to upper case
 */

char * ARGS_ON_STACK 
strlwr(s)
	char *s;
{
	char c;
	char *old = s;

	while ((c = *s) != 0) {
		if (isupper(c)) {
			*s = _tolower(c);
		}
		s++;
	}
	return old;
}

char * ARGS_ON_STACK 
strupr(s)
	char *s;
{
	char c;
	char *old = s;

	while ((c = *s) != 0) {
		if (islower(c)) {
			*s = _toupper(c);
		}
		s++;
	}
	return old;
}

#ifdef OWN_LIB

/*
 * Case sensitive comparison functions.
 */

int
strncmp(str1, str2, len)
	register const char *str1, *str2;
	register int len;
{
	register char c1, c2;

	do {
		c1 = *str1++;
		c2 = *str2++;
	} while (--len >= 0 && c1 && c1 == c2);

	if (len < 0) return 0;

	return c1 - c2;
}

int
strcmp(str1, str2)
	const char *str1, *str2;
{
	register char c1, c2;

	do {
		c1 = *str1++;
		c2 = *str2++;
	} while (c1 && c1 == c2);

	return c1 - c2;
}


/*
 * some standard string functions
 */

char *
strcat(dst, src)
	char *dst;
	const char *src;
{
	register char *_dscan;

	for (_dscan = dst; *_dscan; _dscan++) ;
	while ((*_dscan++ = *src++) != 0) ;
	return dst;
}

char *
strcpy(dst, src)
	char *dst;
	const char *src;
{
	register char *_dscan = dst;
	while ((*_dscan++ = *src++) != 0) ;
	return dst;
}

char *
strncpy(dst, src, len)
	char *dst;
	const char *src;
	int len;
{
	register char *_dscan = dst;
	while (--len >= 0 && (*_dscan++ = *src++) != 0)
		continue;
	while (--len >= 0)
		*_dscan++ = 0;
	return dst;
}

int
strlen(scan)
	const char *scan;
{
	register const char *_start = scan+1;
	while (*scan++) ;
	return (int)((long)scan - (long)_start);
}

/*
 * strrchr: find the last occurence of a character in a string
 */
char *
strrchr(str, which)
	const char *str;
	register int which;
{
	register unsigned char c, *s;
	register char *place;

	s = (unsigned char *)str;
	place = 0;
	do {
		c = *s++;
		if (c == which)
			place = (char *)s-1;
	} while (c);
	return place;
}

unsigned char _ctype[256] =
{
	_CTc, _CTc, _CTc, _CTc,				/* 0x00..0x03 */
	_CTc, _CTc, _CTc, _CTc,				/* 0x04..0x07 */
	_CTc, _CTc|_CTs, _CTc|_CTs, _CTc|_CTs,		/* 0x08..0x0B */
	_CTc|_CTs, _CTc|_CTs, _CTc, _CTc,		/* 0x0C..0x0F */

	_CTc, _CTc, _CTc, _CTc,				/* 0x10..0x13 */
	_CTc, _CTc, _CTc, _CTc,				/* 0x14..0x17 */
	_CTc, _CTc, _CTc, _CTc,				/* 0x18..0x1B */
	_CTc, _CTc, _CTc, _CTc,				/* 0x1C..0x1F */

	_CTs, _CTp, _CTp, _CTp,				/* 0x20..0x23 */
	_CTp, _CTp, _CTp, _CTp,				/* 0x24..0x27 */
	_CTp, _CTp, _CTp, _CTp,				/* 0x28..0x2B */
	_CTp, _CTp, _CTp, _CTp,				/* 0x2C..0x2F */

	_CTd|_CTx, _CTd|_CTx, _CTd|_CTx, _CTd|_CTx,	/* 0x30..0x33 */
	_CTd|_CTx, _CTd|_CTx, _CTd|_CTx, _CTd|_CTx,	/* 0x34..0x37 */
	_CTd|_CTx, _CTd|_CTx, _CTp, _CTp,		/* 0x38..0x3B */
	_CTp, _CTp, _CTp, _CTp,				/* 0x3C..0x3F */

	_CTp, _CTu|_CTx, _CTu|_CTx, _CTu|_CTx,		/* 0x40..0x43 */
	_CTu|_CTx, _CTu|_CTx, _CTu|_CTx, _CTu,		/* 0x44..0x47 */
	_CTu, _CTu, _CTu, _CTu,				/* 0x48..0x4B */
	_CTu, _CTu, _CTu, _CTu,				/* 0x4C..0x4F */

	_CTu, _CTu, _CTu, _CTu,				/* 0x50..0x53 */
	_CTu, _CTu, _CTu, _CTu,				/* 0x54..0x57 */
	_CTu, _CTu, _CTu, _CTp,				/* 0x58..0x5B */
	_CTp, _CTp, _CTp, _CTp,				/* 0x5C..0x5F */

	_CTp, _CTl|_CTx, _CTl|_CTx, _CTl|_CTx,		/* 0x60..0x63 */
	_CTl|_CTx, _CTl|_CTx, _CTl|_CTx, _CTl,		/* 0x64..0x67 */
	_CTl, _CTl, _CTl, _CTl,				/* 0x68..0x6B */
	_CTl, _CTl, _CTl, _CTl,				/* 0x6C..0x6F */

	_CTl, _CTl, _CTl, _CTl,				/* 0x70..0x73 */
	_CTl, _CTl, _CTl, _CTl,				/* 0x74..0x77 */
	_CTl, _CTl, _CTl, _CTp,				/* 0x78..0x7B */
	_CTp, _CTp, _CTp, _CTc,				/* 0x7C..0x7F */

	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80..0x8F */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90..0x9F */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0..0xAF */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0..0xBF */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0..0xCF */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0..0xDF */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0..0xEF */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  /* 0xF0..0xFF */
};

int toupper(c)
	int c;
{
	return(islower(c) ? (c ^ 0x20) : (c));
}

int tolower(c)
	int c;
{
	return(isupper(c) ? (c ^ 0x20) : (c));
}

/*
 * converts a decimal string to an integer
 */

long
atol(s)
	const char *s;
{
	long d = 0;
	int negflag = 0;
	int c;

	while (*s && isspace(*s)) s++;
	while (*s == '-' || *s == '+') {
		if (*s == '-')
			negflag ^= 1;
		s++;
	}
	while ((c = *s++) != 0 && isdigit(c)) {
		d = 10 * d + (c - '0');
	}
	if (negflag) d = -d;
	return d;
}

#endif /* OWN_LIB */
