/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

#ifndef lint
static char    *rcsid = "$Header:rbutils.c 12.1$";
#endif



#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <dos.h>
#include <io.h>
#include <sys\types.h>
#include "pcparam.h"
#include "rb.h"
#include "trace.h"
#include "vars.h"
#include "dio.h"
#include <ctype.h>

#define NULLTYPE 0
#define INTTYPE  1
#define LONGTYPE 2
#define CHARTYPE 3

#ifdef OS2_CODE
long lcvttonum(char *);
#endif

struct defaults
{
	int             dtype;
	union addr
	{
		u_short        *saddr;
		u_long         *laddr;
		char           *caddr;
	}               addr;
	char           *fmt;
	char           *cmd;
};

int	cpu_type = 0;

static int      maxdefaults;
static char far *sptr = (char far *) 0xb8000000;


extern char     bootfile[];
extern char     banner[];
extern char     opdisk_name[];
extern char     kbdfile[];

extern u_long   pc_base;
extern u_short	restart;

#ifdef DEBUG


u_long         option_flag = 0;
u_short		dst_flag = 0;

#define NULLTYPE 0
#define INTTYPE  1
#define LONGTYPE 2
#define CHARTYPE 3
#define BITTYPE 4

char           *types[] = {"", " %d", " %d", " %s", " %b"};

struct defaults defaults[] =
{
    NULLTYPE, (u_short *) 0, " Go - start execution",            "go",
        INTTYPE, &intlv_flag, " Memory Interleave [%d]",         "interleave",
        INTTYPE, &dump_flag, " Memory Dump after load  [%d]",    "dump",
        LONGTYPE, (u_short *) &debug, " Debug messages [%#lx]",  "debug",
        LONGTYPE, (u_short *) &option_flag, " Optional flags [%#lx]", "flags",
        CHARTYPE, (u_short *)bootfile, " Unix File to Load: %s", "file",
        INTTYPE, &memclear, " Clear Coprocessor Memory [%d]",    "clear",
        INTTYPE, &r_base, " PCIF port base [%#x]",         	 "port",
        LONGTYPE, (u_short *)&pc_base, " PCIF address [%#lx]",	 "address",
        CHARTYPE, (u_short *)banner, " banner file: %s",	 "banner",
        INTTYPE, &dst_flag, " Local time is in DST [%d]",	 "dst",
#ifndef OS2_CODE
        INTTYPE, &def_scan, " Scan Code set (DOS) [%d]",         "scancode",
        BITTYPE, &irq_bits, " Irq numbers (Coprocessor IRQs) [", "irqnumbers",
        CHARTYPE, (u_short *)kbdfile, " input script: %s",	 "input",
        CHARTYPE, (u_short *)opdisk_name, " optical disk: %s",	 "opdisk",
        INTTYPE, &restart, " Restart [%d]",		         "restart",
#endif
#if NCPU > 1
        INTTYPE, &ncpu, " number of cpu's [%d]",		 "cpus",
#endif /* NCPU */
        NULLTYPE, (u_short *) 0, (char *) 0,                      0,
};
#endif /* DEBUG */




/*
 * Code
 */

#ifndef OS2_CODE

/*************************************************************************
 * pcpanic - Print a message then exit.
 *
 */

void
pcpanic(s)
	char           *s;
{
	while (1)
	{
		/* in case it happens on interupt level */
		rbprintf("Fatal pc code error!, %s\n", s);
		undo();		/* restart state */
		exit(-1);
	}

}


/*
 * This routine converts a long segmented address to a linear address.
 * The function also returns the segment and offset.
 */

u_long
physaddr(saddr, seg, offset)
	u_long          saddr;	/* Segmented address */
	u_short far    *seg;	/* Put segment here  */
	u_short far    *offset;	/* Put offset  here  */

{

	/*
	 * On Intel machines a long int is physically stored as [low
	 * word:high word]. 
	 *
	 */

	union
	{
		u_long          segaddr;	/* Segmented address */
		struct
		{
			u_short         offset,	/* Logical low order 16 bits     */
			                seg;	/* Logical high order 16 bits */
		}               os;
	}               obj;


	obj.segaddr = (u_long) saddr;
	if (seg != (u_short *) 0)
		*seg = obj.os.seg;
	if (offset != (u_short *) 0)
		*offset = obj.os.offset;
	return ((u_long) ((u_long) obj.os.seg << 4) + obj.os.offset);
}

u_long
segaddr(paddr)
	u_long          paddr;
{

	union
	{
		u_long          segaddr;	/* Segmented address */
		struct
		{
			u_short         offset,	/* Logical low order 16 bits     */
			                seg;	/* Logical high order 16 bits */
		}               os;
	}               obj;

	obj.os.seg = (u_short) ((paddr >> 4) & 0xffff);
	obj.os.offset = (u_short) (paddr & 0x0f);

	return (obj.segaddr);
}


void
clear_screen()
{

	register int    i;
	union REGS      inregs;
	register char  *cp = (char *) &inregs;

	for (i = 0; i < sizeof(union REGS); i++)
		*cp++ = 0;

	inregs.h.ah = 6;	/* Scroll active page up */
	inregs.h.al = 0;	/* Blank Entire window    */
	inregs.h.bh = 7;	/* Normal attribute      */
	inregs.h.ch = 0;
	inregs.h.cl = 0;
	inregs.h.dh = 24;
	inregs.h.dl = 79;
	int86(0x10, &inregs, &inregs);

	inregs.h.ah = 2;	/* Set cursor position   */
	inregs.h.dh = 0;
	inregs.h.dl = 0;
	inregs.h.bh = 0;
	int86(0x10, &inregs, &inregs);

}

#ifdef DEBUG


void
printdescriptor(dt)
	struct DESC far *dt;
{

	u_short         seg, offset;
	u_long          addr;

	addr = physaddr((u_long) dt, (u_short far *) &seg, (u_short far *) &offset);
	printf("Descriptor Table at 0x%x:0x%x (0x%lx)\n", seg, offset, (long) addr);
	printf("\tLimit = %x, basel = %x, baseh = %x, acc = %x\n", dt->lmt, dt->basel,
	       dt->baseh, dt->acc);
}
#endif /* DEBUG */
#endif /* OS2_CODE */

#ifdef DEBUG
void
show_defaults(msg)
	char	       *msg;
{
	register int    i;
	register char  *p;

	printf("\n\t%s\n", msg);
	for (i = 0; p = defaults[i].fmt; ++i)
	{
		printf("%2d: ", i);
		prtype(p, defaults[i].dtype, &defaults[i].addr);
		printf("\n");
	}
	maxdefaults = i;

}

prtype(p, type, addr)
	char           *p;
	union addr     *addr;
{
	switch (type)
	{
	case NULLTYPE:
		printf(p);
		break;

	case CHARTYPE:
		printf(p, addr->caddr);
		break;

	case LONGTYPE:
		printf(p, (long) *addr->laddr);
		break;

	case INTTYPE:
		printf(p, *addr->saddr);
		break;

	case BITTYPE:
		{
			int             j, n = 0;
			printf(p, *addr->saddr);
			for (j = 0; j < 16; ++j)
				if (*addr->saddr & (1 << j))
					printf("%s%d", n++ == 0 ? "" : ",", j);
			printf("]");
		}
		break;

	default:
		break;
	}
}

int
change_defaults()
{
	register int    i;

	char            cbuf[80];

	putchar('\n');

again:

	printf("Which Option ==> ");
	gets(cbuf);
	if (*cbuf != 0)
	{
		if (isalpha(cbuf[0]))
			i = get_cmd(cbuf);
		else
			i = (u_short) atoi(cbuf);
		if ((i < 0) || (i > maxdefaults))
		{
			printf("\r");
			goto again;
		}
		if (i > 0)
		{
			printf("\n");
			printf("%2d: ", i);
			prtype(defaults[i].fmt, defaults[i].dtype, &defaults[i].addr);
			printf(":   ");

			gets(cbuf);

			if (*cbuf != 0)
				change_value(i, cbuf);
		}
	}
#ifndef OS2_CODE
	*dbugptr = debug;
#endif /* OS2_CODE */
	return (i);
}

change_value(i, cbuf)
	char           *cbuf;
{
	int             count = 1;
	register int    k;
	u_long          lval;
	switch (defaults[i].dtype)
	{
	case CHARTYPE:
		strcpy(defaults[i].addr.caddr, cbuf);
		break;

	case NULLTYPE:
		count = 0;
		break;

	case LONGTYPE:
		lval = lcvttonum(cbuf);
		*defaults[i].addr.laddr = lval;
		break;

	case INTTYPE:
		k = cvttonum(cbuf);
		*defaults[i].addr.saddr = k;
		break;

	case BITTYPE:
		for ( ;cbuf[0]; )
		{
			if (cbuf[0] == '-')
			{
				k = cvttonum(cbuf + 1);
				*defaults[i].addr.saddr &= ~(1 << k);
			} else if (cbuf[0] == '=')
			{
				k = cvttonum(cbuf + 1);
				*defaults[i].addr.saddr = k;
			} else
			{
				k = cvttonum(cbuf);
				*defaults[i].addr.saddr |= 1 << k;
			}
			if (cbuf = strchr(cbuf,','))
				++cbuf;
		}
		break;
	default:
		break;
	}
	return (count);
}

/*
 * process arguments and return a flag that indicates that a "go"
 * was processed.
 */
do_args(argv, argc)
	char          **argv;
{
	int             i, j;
	int             go = 0;

	for (j = 1; j < argc; ++j)
	{
		i = get_cmd(argv[j]);
		if (i < 0)
		{
			printf("%s: invalid option: %s\n", argv[0], argv[j]);
			usage(argv[0]);
			exit(-1);
		}
		if (defaults[i].dtype != NULLTYPE)
			change_value(i, argv[++j]);
		if (i == 0)
			go = 1;
	}
	return (go);
}

get_cmd(name)
	char           *name;
{
	int             i;
	int             len;
	char	       *s;

	for (s=name; *s; ++s)
		if (isupper(*s))
			*s = tolower(*s);
		else if (!isalpha(*s))
			break;
	len = s-name;
	for (i = 0; defaults[i].fmt; ++i)
		if (strncmp(name, defaults[i].cmd, len) == 0)
			return (i);
	return (-1);
}

usage(name)
	char           *name;
{
	int             i;

	printf("Usage:\n%s", name);
	for (i = 0; defaults[i].fmt; ++i)
		printf(" %s%s", defaults[i].cmd, types[defaults[i].dtype]);
	printf("\nWhere:\n");
	for (i = 0; defaults[i].fmt; ++i)
		printf("%s      %s\n", defaults[i].cmd, defaults[i].fmt);
}

#ifndef OS2_CODE
void
screen_trace(flag)
	int             flag;
{


	*sptr = flag;
	if ((sptr += 2) > (char far *) 0xb800009e)
		sptr = (char far *) 0xb8000000;
}
#endif /* OS2_CODE */

#endif /* DEBUG */

#ifndef OS2_CODE

int
bcd_unpack(bcddig)
	char            bcddig;
{

	return (((((bcddig & 0xf0) >> 4) * 10) + (bcddig & 0x0f)));
}

/*
 * reboot - Do a HARD reboot of the system.
 *
 *    The BIOS reboot function INT 0x19 is not used here because it
 *    does not cause the POST sequence to run again. To do a reboot
 *    we make a BLOCKMOVE request with the SYSFLAG turned OFF, this
 *    causes a complete reset of the system.
 */
void
reboot()
{

	RSETBIT(r_base+P_CTRL, P_2WIND);	/* Disable first two windows */
	RSETBIT(r_base+P_CTRL, P_4WIND);	/* Disable next two windows */

	reset_sysflag();

	printf("Reboot \n");
	bpcifmove(WRITE, (char far *) 0, (u_long) 0, 2096);

	printf("Reboot failed\n");
	undo();
	rreboot();		/* try the bios call */
	getchar();
	exit(0);
}

/*
 * set the model_80 global to true if the source machine is a
 * model 80
 */
struct info {
	unsigned short	count;
	unsigned char	model;
#define PC_386	0xf8
#define PC_AT	0xc0
	unsigned char	submodel;
	unsigned char 	revision;
	unsigned char	feature;
};

void
get_machine_type()
{
	union REGS regs;
	struct SREGS sregs;
	unsigned long es;
	struct info far *config;
	unsigned char model;

	regs.h.ah = 0xc0;
	segread(&sregs);
	int86x(0x15,&regs,&regs,&sregs);
	es= (unsigned long) sregs.es;
	if (regs.h.ah != 0) {
		cpu_type = OLD_AT;
		return;
	}
	config = (struct info far *)((es << 16) | regs.x.bx);
	if (config->model == PC_386)
		cpu_type = MODEL_80;
	else if (config->model == PC_AT)
		cpu_type = AT;
}
#ifdef DEBUG
extern struct XCLOCK xclock;

/* 
 * place information into the trace buffer.
 */
trace(type, info, size)
	int		type;
	char far       *info;
	int		size;
{
	int i;
	unsigned char far *p;

	if (!tracing)
		return;
	trace_ptr->type = type;
	trace_ptr->time = xclock.sec + xclock.min*60 + (xclock.hrs%12) * 3600;
	if (size > TRACE_DATA)
		size = TRACE_DATA;
	for (p = trace_ptr->info, i=0; i<size; ++i)
		*p++ = *info++;		/* copy trace data */
	for ( ; i<TRACE_DATA; ++i)
		*p++ = 0;		/* zero the rest of it */
	if (trace_ptr == trace_last)
		trace_ptr = trace_buf;
	else
		++trace_ptr;
}

/*
 * invoked out of interrupt level to print out the trace buffer 
 * usually invoked when the system is hung in some fashion
 * we redefine printf so that we can output at interrupt level
 * we don't print empty slots and we only display 20 entries at
 * a time.
 */
#define printf rbprintf
trace_print()
{
	static struct tracebuf far *trace_prt = 0;
	int i;

	if (trace_prt == 0)
		trace_prt = trace_ptr;
	for (i=0; i<20; ) {
		if (trace_prt-- == trace_buf) {
			trace_prt = trace_last;
			printf("\n");
			if (i == 0)
				break;	/* prevent looping */
		}
		if (trace_prt->type) {
			print_trace(trace_prt);
			++i;
		}
	}
}

static char *trace_types[] = {
"", "cbcbq", "int", "intq", "cbcb", "disk", "opdisk", "ub", "tape", "cbcbi",
"hrdwint", "kbdint", "asyint" };

static char *int3irqs[16] = { 
"irq0", "kls", "opdisk", "ub", "irq4", "disk", "fd", "lp" };          

static char *int4irqs[16] = { 
"irq8", "lan0", "lan1", "irq11", "irq12", "irq13", "irq14", "irq15" };

static char *flag_type[] = {
"C", "1", "P", "3", "Ac", "5", "Z", "S", "T", "I", "D", "O", "I/O1",
"I/O2", "NT", "15"};

static char *pc_op(int);
static char *pr_int(int, int);
static char *disk_op(int);

static print_trace(t)
struct tracebuf far *t;
{
	int i;
	u_short time = (t->time);
	u_short hr = time/3600;
	u_short min = time%3600;
	u_short sec = min%60;

	min /= 60;

	printf("%02d:%02d:%02d %02d %-6s ", hr, min, sec,  t->type, trace_types[t->type]);
	switch(t->type)
		{
	case TRACE_CBCB:
		printf("%5d %-8.8s ", t->info[0], pc_op(t->info[0]));
		break;
	case TRACE_CBCBQ:
		printf("%5d %-8.8s ", t->info[1], pc_op(t->info[1]));
		break;
	case TRACE_INT:
	case TRACE_QINT:
		printf("%d %02x %s %d", t->info[0], t->info[2],
			pr_int(t->info[0], t->info[2]), IRQ_GET_INFO(t->info[2]));
		break;
	case TRACE_KBDINT:
	case TRACE_HDWINT:
	case TRACE_CBCBI:
	case TRACE_ASY:
		{
			struct int_trace {
				u_short	sp;
				u_short	ss;
				u_short	ip;
				u_short	cs;
				u_short flags;
				u_short data;
			} far *it = (struct int_trace far *)&t->info[0];
			int first = 1;

			printf("%04x:%04x %04x:%04x <",it->cs,it->ip,it->ss,
									it->sp);
			for (i=0; i < sizeof(u_short)*8; i++) {
				if (it->flags & (1 << i)) {
					if (first) {
						printf("%s",flag_type[i]);
						first = 0;
					} else {
						printf(" %s",flag_type[i]);
					}
				}
			}
			printf("> %02x",it->data);
		}
		break;
	case TRACE_DISK:
	case TRACE_TAPE:
		{
			pcdpl_t far *dplptr = (pcdpl_t far *)&t->info[0];

			printf("%d %s %d (%d,%d,%d) %d",exchw(dplptr->op_code),
			   disk_op(exchw(dplptr->op_code)),exchw(dplptr->drive),
				exchw(dplptr->cyl),exchw(dplptr->head),
				exchw(dplptr->sector),exchw(dplptr->number));
		}
		break;
	case TRACE_UB:
		printf("%02x",t->info[0]);
		break;
	default:
		for (i=0; i<TRACE_DATA; ++i)
			printf("%02x ",t->info[i]);
		}
	printf("\n");
}

static char *disk_op(op)
int	op;
{
	switch (op) {
	case DISKOP_RESET:
		return("reset");
	case DISKOP_STATUS:
		return("status");
	case DISKOP_READ:
		return("read");
	case DISKOP_WRITE:
		return("write");
	case DISKOP_FORMAT:
		return("format");
	case DISKOP_OPEN:
		return("open");
	case DISKOP_CLOSE:
		return("close");
	default:
		return("unknown");
	}
}

static char *pc_op(op)
int	op;
{
	switch (op) {
	case CB_SETMAP: return("SETMAP");
	case CB_REBOOT: return("REBOOT");
	case CB_TODREAD: return("TODREAD");
	case CB_TODRESET: return("TODRESET");
	case CB_HALT: return("HALT");
	case CB_RESTART: return("RESTART");
	case CB_QEOI: return("QEOI");
	case CB_EOI3: return("EOI3");
	case CB_EOI4: return("EOI4");
	case CB_TAPEREQ: return("TAPEREQ");
	case CB_HDREQ: return("HDREQ");
	case CB_OPREQ: return("OPREQ");
	case CB_PUTC: return("PUTC");
	case CB_SUSPEND: return("SUSPEND");
	case CB_FDREQ: return("FDREQ");
	case CB_UBREQ: return("UBREQ");
	case CB_BBT: return("BBT");
	case CB_BIOSREQ: return("BIOSREQ");
	case CB_AFIREQ: return("AFIREQ");
	case CB_MSREQ: return("MSREQ");
	case CB_KBREQ: return("KBREQ");
	case CB_SPKREQ: return("SPKREQ");
	case CB_MASKREQ: return("MASKREQ");
	case CB_MCREQ: return("MCREQ");
	default: return("");
	}
}

static char *pr_int(lvl, irq)
int	lvl, irq;
{
	switch(lvl)
		{
	case 3:
		return(int3irqs[irq&0x7]);
	case 4:
		return(int4irqs[irq&0x7]);
	default:
		return("unknown");
	}
}

#undef printf
#endif /* DEBUG */
#endif /* OS2_CODE */

int cvttonum(cptr)
	char           *cptr;
{
	int             rval = -1;

	char           *cptr1;

	if ((cptr1 = strpbrk(cptr, "xabcdef")) != NULL)
	{
		cptr1 = strpbrk(cptr, "123456789abcdefABCDEF");
		sscanf(cptr1, "%x", &rval);
	} else
		sscanf(cptr, "%d", &rval);

	return (rval);
}


long
lcvttonum(cptr)
	char           *cptr;
{


	long            rval = -1;
	char           *cptr1;

	if ((cptr1 = strpbrk(cptr, "xabcdef")) != NULL)
	{
		cptr1 = strpbrk(cptr, "123456789abcdefABCDEF");
		sscanf(cptr1, "%X", &rval);
	} else
		sscanf(cptr, "%D", &rval);

	return (rval);
}

