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

/* $Header:unix.c 12.2$ */
/* $ACIS:unix.c 12.2$ */
/* $Source: /ibm/acis/usr/sys/pc_code/RCS/unix.c,v $ */

static char *rcsid = "$Header:unix.c 12.2$";


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


/*
 * The address of the cbcb must be on a double word boundary (the
 * lower 2 bits must be 0) for things to work correctly from the
 * Unix side. Rather then move this data to an assembler file,
 * or use some of the strange compiler options (which all have
 * other side affects) we declare a char buffer that is the size
 * of the cbcb plus 16 bytes (12 move bytes then we need just for
 * safety sake) then we get a cbcb pointer into this buffer.
 * At runtime we adjust the address to be on the correct boundary.
 */

char            cbcbbuf[NCPU * sizeof(struct cbcb) + 16] = { 0 };
struct cbcb *cbcbptr = (struct cbcb *) cbcbbuf;
struct cbcb *cur_cbcb,		/* cbcb for current request */
		*end_cbcb,	/* for scanning cbcb's */
		*screen_cbcb;	/* cbcb for screen, keyboard and mouse */

pcplq_t         pcplqaddr_dos[QSIZE] = {0};	/* The queue used for PS/2
						 * ops */

/*
 * the following structure contains far pointers to important
 * data structures so that we can find these from the other side
 * for debugging purposes.
 */

struct debug_info debug_info[] = {
{	(char far *) pcplqaddr_dos,	0,  sizeof (pcplq_t), QSIZE }, 
{	(char far *) &trace_ptr,	0,  sizeof trace_ptr, 1 }, 
{	(char far *) &trace_buf,	0,  sizeof trace_buf, 1 }, 
{	(char far *) &trace_last,	0,  sizeof trace_last, 1 }, 
{	(char far *) 0, 0, 0, 0 }
};

int debug_size = sizeof debug_info;

/*
 * The following structure is used to hold the addresses of our DPL
 * queue entries. The actual addresses for the queue entries are generated
 * dynamically at run time.
 */

pcplqaddr_t     pcplq[QSIZE] = {0};

u_short         r_base = 0x1e0,	/* PCIF IO port base default   */
                pcrwindows = 4,	/* Number of PS to ROMP windows */
                physmem = 200,	/* Size of coprocessor memmory in K */
                physmem_reg = 0,/* Size of coprocessor memmory from
				   the PCIF config register.   */
                pcpl_head = 0,	/* Next available pcpl we can use */
                pcpl_tail = 0,	/* Next one to work on      */
		pc_mem_size = 0, /* pc's memory size */
		ncpu = 1,	/* number of CPU's */
                memclear = 1;	/* Clear coprocessor memory */

/* Some variables for the UB ethernet card */

ubq_t           ubq[QSIZE] = {0};
u_short         ub_head = 0;
u_short         ub_tail = 0;
extern UB_SOFTC ub_softc[MAX_UBS];
extern struct vga_params *vga;

#ifdef STATIC_BUFFERS
/*
 * allocate buffer in far bss pool - this isn't usually done
 * because it takes up lots of space in the unix.exe
 */
char far buffer_dio[65535];	/* space for hd buffer pool */
char far buffer_fddio[65535];	/* space for fd buffer pool */
#else
#if defined(MS_CL_5) || defined(IBM_C_2)
/*
 * if we are using Microsoft Version 5 then we can use halloc.
 */
#include <malloc.h>
#endif /* MS_CL_5 */
#endif /* STATIC_BUFFERS */

char far       *buff_dio = {0};	/* Pointer to hard disk transfer area */
#ifdef ABIOS
char far       *buff_fdio = {0}; /* Pointer to fd disk transfer area */
#endif

char            bootfile[] = "boot             ";
char            kbdfile[32] = "";	/* keyboard script file */
char            banner[32] = "";	/* banner message file */

struct bios_data far *bios_data = (struct bios_data far *) BIOSDATA;
struct loadall_area far *loadall_area = (struct loadall_area far *) LOADALLAREA;

u_long          physmem_bytes = 0,	/* Size of ROMP memory in bytes  */
                pc_base = 0x600000;	/* Starting address of PCIF      */

/*
 * Set up a structure to address all of PC memory (less than 1 MB)
  
 */

union pcmem far *pcmem = (union pcmem far *) 0L;

#ifdef DEBUG

u_short         intlv_flag = 1,
		dump_flag = 0,
		blipcntr = 0;
u_long		debug = BLIPDEBUG,
		*dbugptr = &debug;	/* Pointer to debug field in cbcb
					   after we've initialized */

char far       *scrnptr = (char far *) 0xb800009e;
#endif /*   DEBUG */

/*
 * following are those IRQ's that are handed to the co-processor for
 * handling. 
 */
u_short         irq_bits =  IRQBIT(4) | IRQBIT(9) | IRQBIT(10) | IRQBIT(11);

u_short         do_reboot = 0;	/* flag to indicate reboot needed */
u_short         do_exit = 0;	/* flag to indicate exit needed */
u_short		running = 0;	/* not yet running */
u_short		restart = 0;	/* if restarting previous session */
u_short		suspending = 0;	/* if we are currently suspending */

long		boot_dev = 0;	/* boot device in RT terms */
#define BOOT_FD	0xf0
#define BOOT_HD	0xd0


#ifdef DEBUG
/*
 * trace buffer definitions
 */
#define TRACE_ADDR	0x58000000L
#define TRACE_SIZE	0x8000L
struct tracebuf far *trace_ptr = (struct tracebuf far *) TRACE_ADDR;
/* start of the buffer */
struct tracebuf far *trace_buf = (struct tracebuf far *) TRACE_ADDR;
/* end of the buffer */
struct tracebuf far *trace_last = (struct tracebuf far *) (TRACE_ADDR + TRACE_SIZE - sizeof (struct tracebuf));
u_short tracing = 0xffff;	/* trace everything */
#endif /* DEBUG */
int	loop_count;
int	can_restart = 0;
u_short	real_stack;

/*
 * main - Initialize the pc code and the coprocessor hardware,
 * load the unix binary, then service ROMP to PC requests.
 *
 */
extern long     atol();
long		getphysmem();

main(argc, argv)
	int             argc;
	char          **argv;
{
	register int    i, drive, fastboot = 0;

	u_long          startaddr,	/* Entry point to Unix binary */
	                lval;
	extern char     version[];

	pcplqaddr_t     pcplqe;	/* Pointer into dpl queue */

	/* set the cpu type global varible */
	get_machine_type();

	pc_base = getphysmem();		/* get end of physical memory */
	ncpu = get_adapter_pos();

	/* build the offset from the far pointers */
	for (i=0; debug_info[i].faraddr; ++i)
		debug_info[i].offset = exchl(physaddr((u_long) debug_info[i].faraddr,0, 0));

#ifdef STATIC_BUFFERS
	/* use static far buffers */
	buff_dio = (char far *) buffer_dio;
#ifdef ABIOS
	buff_fdio = (char far *) buffer_fdio;
#endif
#else
#if defined(MS_CL_5) || defined(IBM_C_2)
	/* 
	 * if we are compiled under Microsoft C we can 
	 * use halloc to allocate buffers as this doesn't use space
	 * in the unix.exe and means that we can use debuggers 
	 * such as codeview.
	 */
	buff_dio = (char far *) halloc(65536L,1);
#ifdef ABIOS
	buff_fdio = (char far *) halloc(65536L,1);
	if (buff_dio == 0 || buff_fdio == 0) 
#else
	if (buff_dio == 0) 
#endif
	{
		printf("couldn't allocate memory for buffers\n");
		exit(1);
	}
/*	printf("buff_dio=%lx\n",buff_dio);	/* debug */
#ifdef DEBUG
	if ((trace_buf = (struct tracebuf far *) halloc(TRACE_SIZE, 1)) == 0) {
		printf("can't allocate %ld bytes for trace buffer\n",TRACE_SIZE);
		tracing = 0;	/* kill all tracing */
	}
	trace_ptr = trace_buf;
	trace_last = trace_buf + (TRACE_SIZE/sizeof (struct tracebuf) - 1);
#endif /* DEBUG */
#else
	/*
	 * use certain high memory address as buffers for disk I/O
	 * etc. on the old C compilers that don't support halloc.
	 */
	buff_dio = (char far *) 0x60000000L;
#ifdef ABIOS
	buff_dio = (char far *) 0x80000000L;
#endif /* ABIOS */
#endif /* MS_CL_5 */
#endif /* STATIC_BUFFERS */

loop:



	clear_screen();
	printf("%s\n", rcsid);
	printf("\n%s\n", version);

/* from our boot name figure out the boot name for coprocessor */
	if (argc > 0 && argv[0][1] == ':')
		switch(argv[0][0])
			{
		case 'A':
		case 'B':
			boot_dev = argv[0][0] - 'A' + BOOT_FD;
			break;
		case 'C':
		case 'D':
			boot_dev = argv[0][0] - 'C' + BOOT_HD;
			break;
			}
	fastboot = do_args(argv, argc);

	if (banner[0])
		do_banner(banner);	/* display banner message */

	if (fastboot)
		show_defaults("Option Values");
	else
		do
		{
			clear_screen();
			printf("\t%s\n", rcsid);
			show_defaults("Change Default Options");
			i = change_defaults();
		} while (i);

	if (option_flag&OPTION_FD)
		boot_dev = BOOT_FD;

	if (option_flag&OPTION_HD)
		boot_dev = BOOT_HD;	/* force hd0 */
	
#ifdef DEBUG
	if (option_flag&OPTION_AT)
		scrnptr = (char far *) 0xb000009e;	/* mono screen addr */
#endif /* DEBUG */

	if (cpu_type == AT)
		option_flag |= OPTION_AT | OPTION_NOMS;

#if NCPU != 1
	if (ncpu < 1 || ncpu > NCPU) {
		printf("number of cpu's (%d) is invalid\n",ncpu);
		exit(1);
	}
#endif

	/*
	 * Make sure the cbcb is on a double word boundary. 
	 */

	cbcbptr = (struct cbcb *) align(cbcbptr,4,int);
	screen_cbcb = cur_cbcb = cbcbptr;	/* init other ptrs */
	end_cbcb = cbcbptr + ncpu;		/* for scanning cbcb's */

	/*
	 * test to see if pc_base is reasonable
	 */
	if ((pc_base & 0x7ffffL) || pc_base < getphysmem()) {
		printf("PCIF address (0x%lx) is invalid\n",pc_base);
		exit(1);
	}

	for (i=0; i<ncpu; ++i)
	{
		int base = r_base + i * 0x10;
		struct cbcb *cbcb = cbcbptr + i;
		/*
		 * test to see if there is a coprocessor card present 
		 */

		if (inp(base+P_CONF) == 0xff)
		{
			printf("No coprocessor card found at 0x%x\n", base);
			exit(1);
		}
		/*
		 * Get size of Coprocessor memory 
		 */

		cbcb->port = base;
		cbcb->cpu = i;
		physmem_reg = inp((base)+P_CONF) & 0x0f;	/* # of meg of ROMP memory */
		physmem = ((u_short) physmem_reg << 10);	/* Size in 1K blocks */
		physmem_bytes = ((u_long) physmem << 10);	/* Size in bytes    */
		cbcb->physmem_bytes = physmem_bytes;
		cbcb->window = pc_base + (i * 0x80000L);	/* locate window */
	}



	/*
	 * Initialize the pcpl queue. 
	 */


	pcplqe = (pcplqaddr_t) PCPLQADDR;
	for (i = 0; i < QSIZE; i++)
	{
		pcplq[i] = pcplqe++;
		pcplq[i]->flags = 0;
	}

	bzero((char *) intque3, sizeof intque3);
	bzero((char *) intque4, sizeof intque4);

	bzero((char *) kbqueue, sizeof kbqueue);

	msqueue.head = 0;
	msqueue.tail = 0;



#ifdef DEBUG

	dbugptr = &cbcbptr->debug;
	*dbugptr = debug;			/* copy value from the menu */


	if (*dbugptr & INITDEBUG)
		for (i = 0; i < QSIZE; i++)
		{
			printf("PCPLQ %d %#lx,  ", i, pcplq[i]);
			if ((i + 1) % 3 == 0)
				putchar('\n');
		}
#endif /* DEBUG */

	if (option_flag & OPTION_PRINT)
		dup2(fileno(stdprn),fileno(stdout));

	for (i=0; i<ncpu; ++i)
		init_wind(cbcbptr[i].port);		/* initialize ROMP windows */
	if (!restart)
	{
		for (i=0; i<ncpu; ++i)
		{
			struct cbcb *cbcb = cbcbptr+i;
			cur_cbcb = cbcb;
			rompinit(cbcb->port,cbcb->window);	/* initialize the ROMP */

			if (memclear)
				init_rmem(cbcb,4);	/* XXX clear ROMP memory */

			if ((drive = open(bootfile, O_BINARY)) < 0)
			{
				printf("Can't open %s", bootfile);
				exit(-1);
			}
			printf("loading %s\n", bootfile);
			/*
			 * Load the boot program into Unix memory 
			 */

			startaddr = loadboot(drive);	/* load romp boot into mem */

			close(drive);
		}
		cur_cbcb = cbcbptr;

		if (kbdfile[0])
			kbd_script(kbdfile);

	}
	/*
	 * intialize the vga pointer etc.
	 * (must do before storing into cbcb)
	 */

	vga_init();

	/*
	 * Fill out the CBCB 
	 */

	for (i=0; i<ncpu; ++i)
	{
		struct cbcb *cbcb = cbcbptr+i;
		init_cbcb(cbcb);
	}

#ifdef ABIOS
	/*
	 * Initialize ABIOS
	 */
	if (abios_init()) {
		printf("abios_init failed\n");
		exit(1);
	}
#endif /* ABIOS */
	/*
	 * Map trap conditions to our handlers 
	 */

	setints();

	/*
	 * Map External Interrupts To Unix 
	 */

	setirqs();

#ifdef ABIOS
	hdinit();	/* must happend before write_config */
	fdinit();
#endif /* ABIOS */

	asy_init();		/* initialize asy */

	/*
	 * Write system configurations info to Unix memory 
	 */

	for (i=0; i<ncpu; ++i)
	{
		cur_cbcb = cbcbptr+i;
		if (i == 1)
			boot_dev ^= 1;		/* flip boot device */
		write_config();
	}

	cur_cbcb = cbcbptr;

	/*
	 * Set up the Romp To PS/2 Interrupt code 
	 */

	if ((option_flag&OPTION_NOINT) == 0)
		setcbcbvec();		/* set the cbcb vector */


	if ((option_flag&OPTION_NOMS) == 0)
	{
		/*
		 * Initialize The Mouse Code 
		 */
		if ((option_flag&OPTION_MSASY))
			atmsinit();		/* mouse on asy line */
		else
			msinit();		/* initialize on PS/2 */
	}

#ifdef DEBUG
	if (dump_flag)
		pcdebug();
#endif /* DEBUG */

	/*
	 * Initialize the keyboard hardware 
	 */

	if ((option_flag&OPTION_ASCII) == 0)
		init_kbd();

	running = 1;			/* or just about to */

	for (i=0; i<ncpu; ++i)
	{
		cur_cbcb = cbcbptr+i;
		if (restart)
			romprestart();
		else
			rompstart(startaddr);	/* start us up! */
	}
	cur_cbcb = cbcbptr;

	loop_count = 0;
	can_restart++;
	real_stack = getss();
	ps2_ops();

	undo();			/* clear our takeover of romp */

	goto loop;		/* re-initialize */
}


/*
 * main loop:
 *
 * waits for things to happen and acts accordingly
 * 1.	op code to go non-zero (in case an interrupt from the
 *	romp is lost)
 * 2.   send a new romp level 3 or 4 interrupt after the romp
 *	has serviced a previous one.
 * 3.	update clock information in shared memory
 * 4.	send keyboard scan codes if in poll mode
 * 5.	send mouse data
 * 6.	handle ethernet requests
 */
ps2_ops()
{
	register u_short s;
	u_short         pcifint;
	int             i;
	struct intque  *qptr;

	struct msqueue *msq = &msqueue;
	struct msdata  *ms = &msdata;
	struct msdata  *mssrcp;
	extern int      newxmt, newrcv;
	extern int	screen_time;
	extern struct XCLOCK xclock;
#ifdef DEBUG
	int             ldebug;

	static struct i_stack fake_stack;

	/*
	 * fake up an "old" stack so trace from
	 * CBCB int trace works..
	 */
	fake_stack.IP = (u_short) ps2_ops;
	fake_stack.CS = getcs();
	fake_stack.SP = (u_short) &ldebug;
	fake_stack.flags = 0;


	ldebug = (*dbugptr & MAINDEBUG);
	DEBUGF(ldebug, printf("ps2_ops:\n"));

#endif /* DEBUG */

	getclock();

	for (;;)
	{
		loop_count = 0;
		sti();

#ifdef DEBUG

		ldebug = (*dbugptr & MAINDEBUG);
#ifdef OLD_BLIP
		if (*dbugptr & BLIPDEBUG)
		{
			if (blipcntr++ > 5000)
			{
				if (*scrnptr != '*')
					*scrnptr = '*';
				else
					*scrnptr = '|';
				blipcntr = 0;
			}
		}
#define BLIP(x)
#endif /* OLD_BLIP */
#define	BLIP(x) if (*dbugptr & BLIPDEBUG) *scrnptr = x
#else	/* DEBUG */
#define BLIP(x)
#endif /* DEBUG */
		BLIP('|');


		if (cur_cbcb->op_code != 0)
		{
			BLIP('*');
			s = spl();
			cbcbreq((struct i_stack far *)&fake_stack);
			DEBUGF(ldebug, printf("Call cbcbreq from ps2_ops\n"));
			splx(s);
		}

		if ((option_flag & OPTION_CLOCK) == 0) {
			BLIP('!');
			s = spl();
			getclock();
			splx(s);
		}

		if (screen_time >= 0 && 
			(screen_time != xclock.sec /* ||
			vga[screen_cbcb->cpu].pc_opcode == 0 */))
		{
			BLIP('V');
			screen_change(0);
		}

		if (do_reboot)
		{
			do_reboot = 0;
			reboot();
		}
		if (do_exit)
		{
			do_exit = 0;
			doexit();

		}
#ifdef ABIOS
		/*
		 * check for hd or fd transfers completed
		 */

		/*
		 * copy hd reads out off of interupt level
		 */
		if (hddptr) {
			u_short	cnt,i=0,rc;
			char	far *pcaddr=buff_dio;

			while ((cnt = hddptr->atr_cnt[i]) > 0) {
				rc = moveout(hddptr->atr_addr[i],
								pcaddr,cnt);
				if (rc < 0)
					break;
				pcaddr += cnt;
				i++;
			}
			hddptr = 0;
			hddone(rc);
		}
		if (fddptr) {
			u_short	cnt,i=0,rc;
			char	far *pcaddr=buff_fdio;

			while ((cnt = fddptr->atr_cnt[i]) > 0) {
				rc = moveout(fddptr->atr_addr[i],
								pcaddr,cnt);
				if (rc < 0)
					break;
				pcaddr += cnt;
				i++;
			}
			fddptr = 0;
			fddone(rc);
		}
#endif /* ABIOS */
		/*
		 * Check the interrupt queues to find out if we need to
		 * forward any pending interrupts. 
		 */

		s = spl();
		pcifint = IOIN(cur_cbcb->port+R_INTR);
		qptr = &intque4[cur_cbcb->cpu];
		if (((pcifint & 0x08) == 0) && (qptr->head != qptr->tail))
		{
			BLIP('4');
			i = qptr->head;
			DEBUGF(ldebug, printf("forward romp level 4, irq %#x,%d, %d\n",
				  qptr->irqlvl[i], qptr->head, qptr->tail));
			rompint4(4,qptr->irqlvl[i], qptr->fromintlvl[i], 0xff, qptr->cbcb[i]);
			i = (i + 1) % MAXINTQ;
			qptr->head = i;
		}
		qptr = &intque3[cur_cbcb->cpu];
		if (((pcifint & 0x10) == 0) && (qptr->head != qptr->tail))
		{
			BLIP('3');
			i = qptr->head;
			DEBUGF(ldebug, printf("forward romp level 3, irq %#x,%d, %d\n",
				  qptr->irqlvl[i], qptr->head, qptr->tail));
			rompint3(3,qptr->irqlvl[i], qptr->fromintlvl[i], 0xff, qptr->cbcb[i]);
			i = (i + 1) % MAXINTQ;
			qptr->head = i;
		}

		splx(s);		/* allow pending interrupts */
		s = spl();

		/*
		 * Now check the scan code queue to find out if there are any
		 * pending scan codes. 
		 */

		if (option_flag&OPTION_ASCII) {
			if (kbhit()) {	/* user has hit a key */
				BLIP('K');
				i = getch();
				if (i == 0) {
					i = getch();
					if (i == 68) {	/* F10 = debug cmd */
						splx(s);
						pcdebug();
						s = spl();
					} else if (i == 67)	/* F9 = INT 0 */
						rompint0();
					else if (i == 66)	/* F8 = next scr  */
						next_screen();
					else if (i == 61)	/* F3 = ^C */
						kbsend(0x03 | (KBD_ASCII<<8), screen_cbcb);
				} else
					kbsend(i | (KBD_ASCII<<8), screen_cbcb);
			}
		}
		check_kbd(ldebug);

		splx(s);		/* allow pending interrupts */
		BLIP('.');
		s = spl();
		/*
		 * Now check the mouse data queue to find out if there is any
		 * pending mouse data. 
		 */

		pcifint = IOIN(r_base+R_INTR);

		msq = &msqueue;
		ms = &msdata;
		if (((pcifint & 0x10) == 0) && (ms->status == 0) && (msq->head != msq->tail))
		{
			BLIP('M');
			i = msq->head;
			mssrcp = &msq->buff[i];
			DEBUGF(ldebug, printf("forward Mouse Data"));
			msforward(mssrcp);
			i = (i + 1) % MAXMSBUFF;
			msq->head = i;
		}
		splx(s);



		/*
		 * Wait for an operation to enter the queue. When it does
		 * service it. NOTE: We are be the only routine that operates
		 * on pcpl_tail 
		 */

		if (pcpl_tail != pcpl_head) {
			BLIP('N');
			next_request();
		}

		/*
		 *     Check to see if there are any Ethernet requests pending
		 */

		s = spl();
		check_ub();
		splx(s);
		if (++cur_cbcb >= end_cbcb)
			cur_cbcb = cbcbptr;
	}
}


/*
 * check to see if any keyboard characters need to be sent
 */
int check_kbd(ldebug)
	int		ldebug;
{
	struct kbqueue *kbq = &kbqueue[screen_cbcb->cpu];
	struct kbdata  *kb = &kbdata[screen_cbcb->cpu];
	int		pcifint = IOIN(screen_cbcb->port+R_INTR);
	int		i, j;

	if ((((pcifint & 0x10) == 0) || screen_cbcb->vec_map[KBIRQ] == 0)
	    && (kb->kbstatus == 0) && (kbq->head != kbq->tail))
	{
		BLIP('K');
		i = kbq->head;
		j = kbq->buff[i];
		DEBUGF(ldebug, printf("forward scancode %#x,%d, %d\n",
				      j, kbq->head, kbq->tail));
		kbforward(j,screen_cbcb);
		i = (i + 1) % MAXKBBUFF;
		kbq->head = i;
	}
}

/*
 * take the next request off the queue and service it
 */

next_request()
{
	int		i;
	int		s;
	register pcplqaddr_t pcplqe;	/* Pointer into dpl queue */

	s = spl();
	pcplqe = pcplq[pcpl_tail];

	/*
	 * If the next buffer is already marked as busy, then
	 * something is wrong. Try to recover by initializing
	 * the queue. 
	 */

	if ((pcplqe->flags & PCPLBUSY) == PCPLBUSY)
	{
		printf("pc code: Disk Queue Corrupted !Reinitialize!\n");
		for (i = 0; i < QSIZE; i++)
			pcplq[i]->flags = 0;
		splx(s);
		return;
	}

	if (pcplqe->flags & PCPLREQUEST)
	{
		cur_cbcb = pcplqe->cbcb;	/* set proper cbcb */
		TRACE(TRACE_CBCB, (char *) pcplqe, sizeof *pcplqe);
		pcplqe->flags |= PCPLBUSY;
		splx(s);

		switch (pcplqe->op)
		{
#ifndef ABIOS
		case CB_HDREQ:
			BLIP('d');
			diskop(pcplqe);
			break;
		case CB_FDREQ:
			BLIP('f');
			diskop(pcplqe);
			break;
#else /* ABIOS */
		case CB_HDREQ:
			hddiskop(pcplqe);
			break;
		case CB_FDREQ:
			fddiskop(pcplqe);
			break;
#endif /* ABIOS */

		case CB_TAPEREQ:
			BLIP('t');
			st_tapeop(pcplqe);	/* tape request */
			break;

		case CB_OPREQ:
			BLIP('o');
			op_diskop(pcplqe);	/* optical disk request */
			break;

		case CB_BIOSREQ:
			BLIP('v');
			if (pcplqe->unix_cb)
				biosreq(pcplqe);	/* old bios request */
			else
				vga_command();		/* new vga request */
			break;

		case CB_MSREQ:
			BLIP('m');
			if (!(option_flag&OPTION_NOMS))
				mscmd(pcplqe);
			else
				mouseint(1,0);		/* pretend it worked */
			break;

		case CB_KBREQ:
			BLIP('k');
			s = spl();
			if ((option_flag&(OPTION_ASCII|OPTION_SCAN1|OPTION_NOKBD|OPTION_NOMS)) ||
					cur_cbcb != screen_cbcb)
				kbsend(0xfa | (KBD_DATA<<8), cur_cbcb);	/* pretend we did it */
			else
				kbdcmd();		/* normal case */
			splx(s);
			break;

		case CB_AFIREQ:
			BLIP('a');
			afireq(pcplqe);
			break;

		case CB_SPKREQ:
			BLIP('s');
			speaker(pcplqe);
			break;

		case CB_MASKREQ:
			BLIP('x');
			if (cur_cbcb->cpu == 0)
				maskint(pcplqe);
			break;

		case CB_TODRESET:
			BLIP('c');
			setclock(pcplqe);
			break;

		case CB_SUSPEND:
			suspend();
			break;

		case CB_IPL:	/* ipl from specified addr */
			if (cur_cbcb->state == STATE_IPL)
			{
				long raddr = pcplqe->unix_cb;
				if (raddr)
					moveout(0L, (char far *) &raddr, 4);
				SETBIT(cur_cbcb->port+R_CTRL, R_IPLC); /* IPL it */
				cur_cbcb->state = STATE_RUNNING;
				state_change(cur_cbcb);
			}
			break;

		case CB_POR:
			if (cur_cbcb->state == STATE_HALT ||
				cur_cbcb->state == STATE_REBOOT)
			{
				rompinit(cur_cbcb->port,cur_cbcb->window);	/* initialize the ROMP */
				reset_queues(cur_cbcb->cpu);
				bzero(&cur_cbcb->vec_map, 16);
				cur_cbcb->state = STATE_IPL;
				state_change(cur_cbcb);
			}
			break;

		case CB_REBOOT:
			BLIP('r');
			cur_cbcb->state = STATE_REBOOT;
			if (cur_cbcb->cpu == 0)
				reboot();	/* We never return from
						 * this */
			state_change(cur_cbcb);
			break;
		
		case CB_HALT:
			BLIP('r');
			cur_cbcb->state = STATE_HALT;
			if (cur_cbcb->cpu == 0)
				doexit();	/* We never return from
						 * this */
			state_change(cur_cbcb);
			break;

		case CB_PUTC:
			BLIP('p');
			putchar((char) pcplqe->unix_cb);
			break;

		case CB_MCREQ:
			BLIP('z');
			mcreq(pcplqe);
			break;

		default:
			printf("pccode: Unknown Op (%d)\n", pcplqe->op);
			break;
		}
	}
#ifdef DEBUG
	else
		printf("pc code: head != tail, but request flag not on!\n");
#endif /* DEBUG */
	BLIP('n');
	s = spl();
	pcplqe->flags = 0;
	pcplqe->op = 0;
	if (++pcpl_tail >= QSIZE)
		pcpl_tail = 0;
	splx(s);
}


/*
 * clear the various queues after a Power On Reset of a cpu
 */
reset_queues(cpu)
int cpu;
{
	bzero((char *) &intque3[cpu], sizeof intque3[cpu]);
	bzero((char *) &intque4[cpu], sizeof intque4[cpu]);
	bzero((char *) &kbqueue[cpu], sizeof kbqueue[cpu]);
	bzero((char *) &kbdata[cpu], sizeof kbdata[cpu]);
}

/* 
 * check to see if there are any ethernet requests pending.
 */
check_ub()
{
	int             unit = 0;
	int		i;

	if (cur_cbcb != cbcbptr)
		return;		/* only master can have a ub device */
	if (ub_tail != ub_head)
	{
		BLIP('U');
		/*
		 * If the next buffer is already marked as busy, then
		 * something is wrong. Re-initialize the queue 
		 */
		if ((ubq[ub_tail].zcbflags & UBBUSY) == UBBUSY)
		{
			printf(" Ub Queue is Corrupted!\n");
			for (i = 0; i < QSIZE; i++)
				ubq[i].zcbflags = 0;
		} else
			if ((ubq[ub_tail].zcbflags & UBREQUEST) ==
			    UBREQUEST)
		{
			ubq[ub_tail].zcbflags |= UBBUSY;
			if (ubop(ubq[ub_tail]) < 0)
				printf("rb.c: calling ubop is messed up\n");
		} else
			printf("head != tail, but request flag not on!\n");
		ubq[ub_tail].zcbflags = 0;
		if (++ub_tail >= QSIZE)
			ub_tail = 0;
	}
	for (unit = 0; unit < MAX_UBS; unit++)
	{
		UB_SOFTC       *us = &ub_softc[unit];

		if (us->newxmt && us->xmtzcb.header.ZCB_Status == 0 ||
		    us->newrcv && us->rcvzcb.header.ZCB_Status == 0)
		{
			BLIP('u');
			if (us->newxmt && us->xmtzcb.header.ZCB_Status == 0)
			{
				us->newxmt = 0;
				sendxmtzcb(unit);
				rompint3(3,
				  FAKEPOLL(UBIRQ)|IRQ_PUT_INFO(UB_XMIT_INTR),
								 0, 0, (struct cbcb *) 0);
			}
			if (us->newrcv && us->rcvzcb.header.ZCB_Status == 0)
			{
				us->newrcv = 0;
				sendrcvzcb(unit);
				rompint3(3,
				  FAKEPOLL(UBIRQ)|IRQ_PUT_INFO(UB_RCV_INTR),
								 0, 0, (struct cbcb *) 0);
			}
		}
	}
}

/*
 * undo everything that has been done that would prevent
 * reexecution of our code or a return to DOS
 */

undo()
{
	register u_short s;
	int i;

	if (!running)
		return;		/* nothing much to do */

	s = spl();

	if (!suspending)
		for (i=0; i<ncpu; ++i)
		{
			cur_cbcb = cbcbptr+i;
			romp_reset();	/* stop romp */
		}
	screen_cbcb = cur_cbcb = cbcbptr;

	if ((option_flag&OPTION_NOINT) == 0)
		resetcbcbvec();		/* reset the cbcb vector */

	if ((option_flag&OPTION_ASCII) == 0)
		reset_kbd();		/* restore keyboard */

	undo_ub();		/* restore ub state */

	resetirqs();		/* restore IRQ vectors */

	resetints();		/* restore trap vectors */

	outpw(0x4ae8,0);	/* reset 8514 */
	set_screen(vga,11);		/* reset 8514 color */

	running = 0;

	splx(s);
}


#define BIOS_SYSSERV 0x15
#define GETMEMFUNCTION	0x88
/*
 * ask bios how much physical memory is available and return it 
 * in bytes rounded up to the next 512k boundary.
 */
long 
getphysmem()
{
	union REGS      inregs, outregs;
	long size;

	inregs.h.ah = GETMEMFUNCTION;
	inregs.x.cflag = 0;
	int86(BIOS_SYSSERV, &inregs, &outregs);
#if !defined(MS_CL_5) && !defined(IBM_C_2)
/*
 * Don't test cflag if using MS version 5.0 as it sets cflag even when
 * there aren't any errors.
 */
	if (outregs.x.cflag) {
		printf("couldn't determine physical memory size\n");
		return(pc_base);
	}
#endif
	pc_mem_size = outregs.x.ax;
	size = 0x100000L + (long)outregs.x.ax * 1024;
	return((size + 0x7ffffL) & ~0x7ffffL);
}

/*
 * print out banner file
 * if the file starts with a digit then delay for that number of seconds
 * afterwards so that there is time to read it.
 */
static do_banner(file)
	char		*file;
{
	int	fd = open(file,0);
	int	length;
	char	buff[128];
	int	delay_time = 5;
	int	flag = 0;
	char	*ptr;
	int	i;

	if (fd < 0) {
		printf("can't open %s\n",file);
		exit(1);
	}
	while ((length = read(fd, buff, sizeof buff)) > 0) {
		ptr = buff;
		if (flag++ == 0)
			if (isdigit(buff[0])) {
				delay_time = *ptr++ - '0';
				--length;
			}
		write(1, ptr, length);
	}
	close(fd);
	if (delay_time)
		for (i=0; i<300; ++i) {
			delay(delay_time);
			if (kbhit())
				break;
		}
}


/*
 * move out restart block to coprocessor memory and then 
 * undo everything (leaving processor running) and exit
 * to DOS.
 */
suspend()
{
	struct restart rest_block;

	rest_block.magic = exchl(RESTART_MAGIC);
	rest_block.counter = 0;
	moveout(RESTART_ADDR, (char far *) &rest_block, sizeof rest_block);
	suspending = 1;
	cur_cbcb->state = STATE_SUSPEND;
	if (cur_cbcb->cpu == 0)
	{
		undo();
		exit(0);
	}
	state_change(cur_cbcb);
}


doexit()
{
	undo();
	if ((option_flag&OPTION_NOEXIT) == 0)
		exit(0);
	for (;;)
		;
}


/*
 * a cpu has changed state. Report this to the other cpu's 
 * that are still running. If a kernel is running it will
 * print a message.
 */
state_change(change_cbcb)
struct cbcb *change_cbcb;
{
	int i;
	struct cbcb *cbcb = cbcbptr;
	for (i=0; i<ncpu; ++i, ++cbcb)
	{
		if (cbcb != change_cbcb && cbcb->state == STATE_RUNNING)
			kbsend((KBD_CPUSTATE<<8) + (change_cbcb->cpu<<4) + change_cbcb->state, cbcb);
	}
	if (change_cbcb->state != STATE_RUNNING)
		mcclear(change_cbcb);	/* mc stopped */
}
