/* crunt2.c	commonly used functions written in c.
 *	MQH 2/82
 *
 * This file contains runtime support functions not written in
 * assembly language. With they are all machine independent,
 * with the exceptions of:	bios()	inp()	outp() exec() execl().
 * This file should be included or linked into most programs, as
 * it eliminates most dependencies between other files in the package.
 */

#define _T	1
#define _F	0


strlen(s)
char * s;
{
	register char *x;

	for(x = s ; *x ; ++x)
		;
	return x-s;
}

isalpha(c)
char c;
{
	register char cc;

	return isupper(cc=c) || islower(cc);
}

isupper(c)
char c;
{
	register char cc;

	return (cc = c) >= 'A' && cc <= 'Z';
}

islower(c)
char c;
{
	register char cc;

	return (cc = c) >= 'a' && cc <= 'z';
}

isdigit(c)
char c;
{
	register char cc;

	return (cc = c) >='0' && c <= '9';
}

iswhite(c)
char	c;
{
	switch(c)
	{
	case ' ':
	case '\t':
	case '\r':
	case '\n':
	case '\f':
		return _T;
	default:
		return _F;
	}
}

toupper(c)
char	c;
{
	register char cc;

	if((cc = c) >= 'a' && cc <= 'z')
		return cc - ' ';
	return cc;
}

strcpy(d,s)
char *d, *s;
{
	register char *dd;

	for(dd = d; *dd++ = *s++;)
		;
	return d;
}

puts(s)
 char *s;
{
	register char *ss;

	for(ss=s;*ss;)
		putchar(*ss++);
}

gets(s)		/* please excuse this function, it knows not what it does */
char *s;
{
	register char c;
	char *temp;
	int count, i;

	temp = s;
start:	count = 0;
	s = temp;
	while ((c=getchar()) != '\n' && c != '\r')
	switch(c)
	{
	case 'U'-'@':
	case 'X'-'@':
		putchar('\n');
		goto start;		/* ugh */
	case 0x7f:
		putchar('\b');
	case '\b':
		putchar(' ');
		putchar('\b');
		if (count) {
			--count;
			--s;
		}
		break;
	 case 'R'-'@':
		putchar('\n');
		for (i = 0; i < count; )
			putchar(temp[i++]);
		break;
	 default:
		*s++ = c;
		++count;
	}
	*s = 0;
	return temp;
}

putchar(c)
char c;
{
	if(c=='\n')
		cconout('\r');
	cconout(c);
}

char *ccungot;			/* points to ungotten character */
				/* We do indeed realize this is */
				/* kludgey, but ...		*/
getchar()
{
	register char c;

	ccungot = "";		/* *ccungot is static, initialized to 0 */
	if(*ccungot) {
		c = *ccungot;
		*ccungot = 0;
	}
	else
		c = cconin();
	if(c == '\r') {
		cconout('\n');
		return '\n';
	}
	else
		return c;
}

/*
 * Note: Some linkage editors only use six chars of a symbol. We suggest
 *	you use this define in you programs to differentiate ungetc() from
 *	ungetchar().
 */ 
#define ungetchar	ugetchar
ungetchar(c)
char c;
{
	*ccungot = c;
}

xmain()
{
	int *gargv;	/* char ** */
	int gargc;

	load_args(&gargc,&gargv);
	main(gargc,gargv);
}

load_args(argc,argv)	/* set up argv and argc for C program */
int *argc,*argv;	/* argv should actually be char **argv */
{
	register int c;
	char *tmp[64];
	char *p, *com_end;

	p = com_line();
	com_end = p + com_len();
	for(tmp[0] = "", c = 1; tmp[c] = nextarg(&p,com_end); ++c)
		;
	*argc = c;
	c = (sizeof "") * (c + 1);
	movmem(tmp, *argv = evnbrk(c), c);
}

nextarg(p2,com_end)
 char **p2;
 char *com_end;
{
	register char *p;
	char *q,dlmt,tmp[128];
	int count;

	for(p = *p2;; ++p){
		if(p>=com_end)
			return _F;
		if(!iswhite(*p))
			break;
	}
	switch(*p) {
	case '\'':
	case '"':
		dlmt = *p++;	break;
	default:
		dlmt = 0;	break;
	}
	for(q=tmp, count=0
		;(dlmt ? *p != dlmt : !iswhite(*p)) && p < com_end; ++count) {
		if(*p == '\\')
			++p; /* should be more elaborate */
		*q++ = *p++;
	}
	*q = 0;
	++count;
	movmem(tmp,q = sbrk(count), count);
	*p2 = p + 1;
	return q;
}

sbrk(p)
 int p;
{
	register char * save;
	extern char * ccedata;

	if(brk((save=ccedata)+p))
		return -1;
	return save;
}

evnbrk(i)
	int i;
{
	sbrk(sbrk(0)&1);
	return sbrk(i);
}

q1(s)
char *s;
{
	puts(s);
	return s;
}

movmem(source,dest,count)
char *dest,*source;
int count;
{
	register char *d;

	if((d = dest) == source || count == 0)
		return d;
	if(d < source)
		ccldir(d,source,count);
	else
		cclddr(d + count - 1,source + count - 1,count);
	return dest + count;	/* dest instead of d because 8080 ccld.r()
				 * trashes the register variable
				 */
}

setmem(dest,count,byte)
 char *dest;
 int count;
 char byte;
{
	register char *d;	/* trashed by ccldir() */

	if(count) {
		*(d = dest) = byte;
		ccldir(d + 1, d, count - 1);
	}
	return dest;
}

/*****************************************************************
**								**
**		MACHINE  DEPENDENT  STUFF			**
**								**
*****************************************************************/

/*	Bios entry points	*/

#define SELDSK		 9
#define SETTRK		10
#define SETSEC		11
#define SETDMA		12
#define READ		13
#define WRITE		14
#define SECTRAN		16
#define GETSEGB		18

int *
bios(jmpnum,bc,de)	/* This version of bios() recognizes that SELDSK
 * and SECTRAN return their values in RP not A and
 * that SECTRAN is passed RS AND RP.
 */
char *jmpnum;
int	bc,de;
{
	extern unsigned bxx;
	struct	para {	
		char	funcnum;
		int	cx, dx;
	} memarea;

	memarea.funcnum = jmpnum;
	memarea.cx = bc;
	memarea.dx = de;

	switch(jmpnum) {
	case SECTRAN:
	case SELDSK:
	case GETSEGB:
		bdos(50,&memarea);
		return bxx;
	default:
		return bdos(50,&memarea);
	}
}

/*
 * inp() and outp() are machine dependent. These are for the 8086 processor
 * They do not work in a split I&D enviornment, though they are ROMable.
 */
struct portdes {
	char	opc;
	char	idn;
} port;


inp(p)
char	p;
{
	port.opc = 0xED;
	port.idn = 0xC3;

	return ccalla(&port, 0, 0, 0, p);
}

outp(c,p)
char c, p;
{
	port.opc = 0xEF;
	port.idn = 0xC3;

	return ccalla(&port, c, 0, 0, p);
}

/*
 * These two functions should work under cpm-86 version 1.1.
 * However they have not been tested since we haven't received
 * our upgrade to 1.1 yet.
 */


#define EXECAREA	(0x0080)
#define EXEC_PROG	47
#define	SET_DMA		26

exec(prog)			/* invoke file prog. default ext ".COM" */
 char *prog;
{
	register char *c;


	/* copy progname until nul or dot */
	for(c = EXECAREA;*prog && *prog != '.';)
		*c++ = *prog++;

	*c++ = ' ';
	*c = 0;

	bdos(SET_DMA, EXECAREA);
	bdos(EXEC_PROG, 0);

	return (-1);
}


execl(nargs)	/* set up command line */
int nargs;	/* then exec */
{
	register char *l,*p;
	int *ip;

	if(nargs < 1)
		return (-1);
	ip = &nargs;

	/* construct command line */
	for(l = EXECAREA; nargs; --nargs) {
		p = ip[nargs];
		while(*p)
			*l++ = *p++;
		*l++ = ' ';
	}
	*l = 0;

	bdos(SET_DMA, EXECAREA);
	bdos(EXEC_PROG, 0);

	return (-1);
}
