/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 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/os2code/RCS/unix.c,v $ */

static char rcs_header[]="$Header:unix.c 12.2$";
static char rcs_source[]="$Source: /ibm/acis/usr/sys/os2code/RCS/unix.c,v $";

/* Copyright University of Southern California, 1988
 * The monitor process is a user level process to handle a unix session
 * under OS/2.
 * It spawns off a number of threads to handle the various services that
 * have to be provided. These services include:
 * Services include (t = thread, p = polled) :
 * 	- hard disk (t)
 *	- floppy disk (t)
 *	- keyboard (p)
 *	- screen (t)
 *	- mouse (p)
 *	- graphics screen (p)
 *	- speaker (p)
 *	- tokenring (p)
 *	- ethernet (p)
 *	- microchannel driver (t)
 * The following are not yet supported:
 *	- printer support
 *	- serial port
 *	- parallel port
 */

#include "pctype.h"
#include <doscall.h>
#include <dos.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include "rb.h"
#include "os2ioctl.h"
#include "dosfile.h"
#include "pcparam.h"
#include "os2data.h"
#include "sem.h"
#include "vga.h"
#include "thread.h"
#include "pcparam.h"

#ifdef PROFILING
#include "profile.h"
#endif

/* #define MAIN_POLL 1	/* */

char		bootfile[64] = BOOT_FILE;
char            banner[32] = "";	/* banner message file */

#define BOOT_FD	0xf0
#define BOOT_HD	0xd0
long		boot_dev = BOOT_HD;	/* boot device in RT terms */
long		debug = 0;
int		memclear;
int		intlv_flag = 1;
int		dump_flag;
int		cpu_number;		/* this processor's cpu number */
int		ncpu;			/* total number of cpu's */
static		int nets_found = 2;
extern	u_long	option_flag;
unsigned int		os2_version;	/* OS/2 version we're using */
u_int		clock_tid;		/* clock thread id */
u_int		main_tid;		/* main thread id */
u_int		main_flag;		/* if main loop is done */
int		not_started = 0;	/* set for 2nd cpu if not started */
int		sleep_tid;		/* tid of thread waiting to start romp*/

extern char version[];

/* loadboot returns the starting address of the ROMP program */
u_long	loadboot();
static void open_device_driver();
static void unix_init();
static int process_driver_input();
void far shutdown();
void far proc_start();

/* the following are valid in the master thread only */
int pid[NCPU];	/* process id of each cpu */
int sid[NCPU];	/* session id of each cpu */

#define MAX_PATH_NAME	1024
#define MAX_ARG_SIZE	1024
#define EXE_SUFFIX	".EXE"
char	unix_path[MAX_PATH_NAME];

/*
 * speaker defines
 */
void far speaker();
#define SPEAKER_SIZE	512
char *spk_stack;

main(argc,argv)
int argc;
char **argv;

{
	int		i;
	int		fastboot;
	char		*tmp_ptr,*malloc();
	char	*unix_exe;

	unix_name = argv[0];	/* save the name of the program */
	/*
	 * now let's build the name required for dos exec program.
	 */
	unix_exe = (char *)malloc(strlen(unix_name)+strlen(EXE_SUFFIX)+1);
	strcpy(unix_exe,unix_name);
	if (strstr(unix_name,".") == 0) {
		/* add uni.exe suffix */
		strcat(unix_exe,EXE_SUFFIX);
	}
	if (strstr(unix_exe,"\\") || strstr(unix_exe,":")) {
		strncpy(unix_path,unix_exe,MAX_PATH_NAME);
	} else {
		DOSSEARCHPATH(0x3,"PATH",unix_exe,unix_path,MAX_PATH_NAME);
	}
	free(unix_exe);


	/* should use an API to get the size of the physical memory */

	pc_base = 0xf00000;

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

	/* open the device driver */
	open_device_driver();

	DOSGETVERSION(&os2_version);

	/* get adapter POS data */
	DOSEXITLIST(1,shutdown);
	if (DOSPORTACCESS(0,0,0x94,0x102)) {
		printf("couldn't get POS port access\n");
	}
	ncpu = get_adapter_pos();
	if (ncpu > 1) {
		option_flag |= OPTION_ONE_DISK;
	}

/* from our boot name figure out the boot name for coprocessor */
	if (argc > 0 && argv[0][1] == ':') {
		int c = argv[0][0];
		if (islower(c))
			c = toupper(c);
		switch(c)
			{
		case 'A':
		case 'B':
			boot_dev = c - 'A' + BOOT_FD;
			break;
		case 'C':
		case 'D':
			boot_dev = c - 'C' + BOOT_HD;
			break;
			}
		}
	/*
	 * tell the device driver were to find the pcif control
	 * register so it can handle interrupts poperly
	 */
	r_base = pcif_get_addr(&cpu_number);
	pc_base -= (((u_long)cpu_number) << 20);

	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", rcs_header);
			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 */
	

	if (cpu_number) {
		/* not master */
		boot_dev ^= 1;		/* flip boot device */
		/* get FLAG_A signal 
		DOSSETSIGHANDLER(proc_start,0,0,2,5); */
	} else if (ncpu > 1) {
		/*
		 * start up sessions for the other processors
		 */
		int	i,arg_length;
		char	**av = argv;
		char	*arg_list,*icon_file;
		char	title[30];

		struct PStartData  {
			u_short 	Length;
			u_short 	Related;
			u_short 	FgBg;
			u_short 	TraceOpts;
			char far	*PgmTitle;
			char far	*PgmName;
			char far	*PgmInputs;
			char far	*TermQ;
			char far	*Environment;
			u_short 	InheritOpt;
			u_short 	SessionType;
			char far	*Iconfile;
			char far	*PgmHandle;
			u_short 	PgmControl;
			u_short 	InitXPos;
			u_short 	InitYPos;
			u_short 	InitXSize;
			u_short 	InitYSize;
		} StartData;

		/*
		 * build the argument list
		 */
		if (arg_list = malloc(MAX_ARG_SIZE)) {
			fatal("not enough space for argument list");
		}
		icon_file = malloc(MAX_PATH_NAME);
		arg_list[0] = 0;
		arg_length = 1;
		while (*av) {
			strncat(arg_list,*av,MAX_ARG_SIZE-arg_length);
			arg_length += sizeof(*av);
			if (arg_length > MAX_ARG_SIZE)
				break;
			strncat(arg_list," ",MAX_ARG_SIZE-arg_length);
			arg_length += 1;
			if (arg_length > MAX_ARG_SIZE)
				break;
			av++;
		}
		

		StartData.Length = sizeof(struct PStartData);
		StartData.Related = 0; /* not related */
		StartData.FgBg = 1; /* start in background */
		StartData.TraceOpts = 0; /* no tracing */
		StartData.PgmName = unix_path;
		StartData.PgmInputs = arg_list;
		StartData.TermQ = 0;	/* no terminate queue */
		StartData.Environment = 0; /* use parent's environment */
		StartData.InheritOpt = 1; /* inherit from us */
		StartData.SessionType = 1; /* full screen window */
		StartData.PgmHandle = 0; /* no program handle created */
		StartData.PgmControl = 0; /* not used (not a window session) */
		StartData.InitXPos = 0; /* not used (not a window session) */
		StartData.InitYPos = 0; /* not used (not a window session) */
		StartData.InitXSize = 0; /* not used (not a window session) */
		StartData.InitYSize = 0; /* not used (not a window session) */


		for (i=1; i < ncpu; i++) {
			/*
			 * build the icon 
			 */
			sprintf(title,"SLAVE%d.ICO",i);
			if (icon_file) {
				DOSSEARCHPATH(0x3,"DPATH",
						title,icon_file,MAX_PATH_NAME);
			}
			/*
			 * build title
			 */
			sprintf(title,"Academic OS 4.3 - Slave %d\n",i);
			StartData.PgmTitle = title;
			StartData.Iconfile = icon_file;
			if (DOSSTARTSESSION(&StartData,&sid[i],&pid[i])) {
				StartData.Length = sizeof(struct StartData);
				DOSSTARTSESSION(&StartData,&sid[i],&pid[i]);
			}

		}
		if (icon_file)
			free(icon_file);
		free(arg_list);
	}
	
	/* print our header 
	printf("USC-ACSC/IBM UNIX Monitor Process\n");
	printf("$Header:unix.c 12.2$\n\n");
	*/
	if (DOSPORTACCESS(0,0,r_base,r_base+0xf)) {
		fatal("couldn't get PCIF port access\n");
	}

	/* 
	 * we now lock the data segment into memory. Some of our routines try
	 * to malloc more data after we are locked. This requires growing our
	 * segment, which we can't do. Instead we first allocate a pool of 
	 * memory, then lock the data segment and free the pool. This allows
	 * malloc to work later. Current we reserve 4 Kbyte for unix.exe work.
	 */
	if ((tmp_ptr = malloc(4*1024)) == 0) {
		fatal("Couldn't malloc enough memory to support unix.exe\n");
	}
	/* any variable in the data segment will work. the ioctl only looks at
	 * the segment.  */
	pcif_data_handle = pcif_lock((char far *)&pcif_data_handle);

	free(tmp_ptr);

	if ((spk_stack = malloc(SPEAKER_SIZE)) == 0) {
		fatal("Could allocate speaker stack\n");
	}

	/* allocate memory for buffers */
	if ( init_data() < 0 ) {
		fatal("Data buffer initialization failed\n",1);
	}

	if (cpu_number == 0) {
		/* initialize the ungerman bass support */
       		if ( ub_init() < 0 ) {
			printf("Lan support not provided\n");
			nets_found--;
        	}

		/* initialize the token ring support */
        	if ( lan_init() < 0 ) {
			printf("Lan support not provided\n");
			nets_found--;
		}
	} else {
		nets_found = 0;
	}

	/* create the disk threads */
	if ( spawn_dio_thread() < 0 ) {
		fatal("Disk thread creation failed - fatal error\n",1);
	}

	/* initialize the screen */
	if ( screen_init() < 0 ) {
		fatal("screen initialization failed\n",1);
	}

	/* initialize the interrupt queues */
	init_intr_queues();

	/* initialize the co-processor */
	unix_init();

	/* initialize the mouse */
	if( mouse_init() < 0 ) {
		printf("mouse initialization failed\n");
	}

	/* initialize the keyboard */
	if ( kbd_init() < 0 ) {
		fatal("keyboard initialization failed",1);
	}

	/* initialize microchannel device */
	if (mcinit() < 0)
		fatal("mc driver initialization failed",1);

	start_clock_thread();

#ifdef PROFILING
	init_profile_timer();
#endif
	/* start keyboard thread */
	start_keyboard();
	
	DEBUGF(cbcbptr->debug & INITDEBUG, initdebug());	/* print thread prios etc */

#ifndef MAIN_POLL
	/* start processing requests */
	start_main_thread();
#endif /* MAIN_POLL */

	/* loop on the driver input */
	process_driver_input();

	/* can only come here if unix requests a shutdown */
	exit(0);
}


/* function to connect to the device driver. It does not return if there
 * is an error.
 */

static void
open_device_driver()
{
	u_int	action;
	u_long	size = 0;
	int	attribute = NORMAL_FILE;
	u_int	openflag = OPEN_IF_EXIST | FAIL_IFN_EXIST;
				/* If file exists open it;
				 * else fail.
				 */
	u_int	openmode = RW_ACCESS | NO_NONE;
				/* Readonly access and shared */
	u_long 	reserved = 0;
	int	rc;

	if ( rc = DOSOPEN(PCIF_DRIVER,(unsigned int far *) &pcif_handle,&action,
			size,attribute,openflag,openmode,reserved) )
		fatal("Unable to open pcif driver",rc);
}	

/* initialize:
 *	- check if coprocessor card present
 *	- get size of coprocessor memory
 *	- set up ROMP windows
 *	- initialize ROMP
 *	- clear ROMP memory
 *	- load the boot program
 *	- write the system configuration into UNIX memory
 *	- initalize the cbcb
 *	- start the ROMP
 */

static void
unix_init()
{
	int	drive;
	u_long	startaddr;

	/*
	 * test to see if there is a coprocessor card present 
	 */

	if (inp(r_base+P_CONF) == 0xff)
	{
		char buf[100];
		sprintf(buf,"No coprocessor card found at 0x%x", r_base);
		fatal(buf,1);
	}

	/*
	 * Get size of Coprocessor memory 
	 */

	physmem_reg = MEMSIZE(r_base);		   /* Size from reg */
	physmem = ((u_short) physmem_reg << 10);   /* Size in 1K blocks */
	physmem_bytes = ((u_long) physmem << 10);  /* Size in bytes */

	init_wind();		/* initialize ROMP windows */

	if( rompinit() != 0 ) 	/* initialize the ROMP */
			fatal("rompinit error",3);
	/*
	 * Without clearing the ROMP memory will cause memory error on PC bus.
	 */

	init_rmem(4);		/* XXX clear ROMP memory */

	/*
	 * load the boot program
	 */
	if ((drive = open(bootfile, O_BINARY)) < 0)
	{
		char buf[100];
		sprintf(buf,"Can't open %s", bootfile);
		fatal(buf,drive);
	}
	printf("loading %s\n",bootfile);
	startaddr = loadboot(drive);	/* load romp boot into mem */
	printf("boot program loaded\n");

	close(drive);

	/* initialize the disks */

	hd_init();
	fd_init();

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

	write_config();

	/*
	 * Fill out the CBCB 
	 */

	init_cbcb();

	/*
	 * wait until we have the input focus to start
	 */
	if (cpu_number != 0) {
		struct ProcIDsArea pids;
		DOSGETPID((struct ProcIDsArea far *) &pids);

		sleep_tid = pids.procid_ctid;
		not_started = 1;
		/*
		 * there is a race condition between
		 * the while, suspend, and the resume in
		 * the signal handler or the vio start.
		 */
		while (not_started) {
			DOSSUSPENDTHREAD(0);
		}
	}

	rompstart(startaddr);	/* start us up! */

	printf("Co-processor initialization complete\n");
}

/*
 * The following routine is very machine/calling sequence
 * dependent. It builds an argument stack for calling thread
 * routines with. The argument list is C calling sequence depended.
 *  it takes:
 *	a pointer to the stack (char far *)
 *	the size of the stack (unsigned short)
 *	the number of word arguments ( unsigned short: note, long arguements
 *		should be treated as 2 shorts arguments).
 *	the argument list.
 * it returns:
 *	the stack pointer to pass to DOSCREATETHREAD (char far *)
 */
char far *
build_c_args(stack,stack_size,arg_count,arg1)
	char far *stack;
	unsigned short stack_size;
	unsigned short arg_count;
	unsigned short arg1;
{
	register unsigned short counter=arg_count;
	register unsigned short *local_stack = &arg1;
	register unsigned short far *remote_stack;
	unsigned short far *stack_top;

	stack_top = (unsigned short far *)(&stack[stack_size-(arg_count)*
						sizeof(unsigned short)]);
	remote_stack = stack_top;

	while(counter--) {
		*remote_stack++ = *local_stack++;
	}
	return((char far *)stack_top);
}

#define CBCB_UNIX_ADDR(index) \
		exchl((u_long)(cbcbptr->cbcb_ent[index].unix_cb))

static int
process_driver_input()

{
	int			rc;
	int			ec;
	u_short	op;
	int	skip_count = 0;
	int	did_work;

#ifdef PROFILING
	u_long	pdi_last_time = elapsed_time();
	u_long	tmp_time;
#endif

	SET_LOW_PRIORITY(PRI_MAIN_LOOP);

	while ( 1 ) {
	     did_work = 0;
#ifdef MAIN_POLL
	     if ( cbcbptr->op_code != 0 ) {
	 	op = exchw(cbcbptr->op_code);
		did_work++;
		if (next_request(op) == 0)
			return(0);
	    }

	    if (nets_found) {
             if ( poll_interrupts() > 0 ) {
                        if ( ++skip_count < 3 )
                                goto skip_kbd_mouse;
                        else
                            skip_count = 0;
             }
	    }
#else
	    DOSSLEEP((long) 100);	/* check every 100ms for polled work */
	    if (main_flag)
		return(0);		/* we're done */
#endif /* MAIN_POLL */

	    if (send_kbd_ack())
		++did_work;
#ifdef POLL_MOUSE
	    if (send_mouse_events())
		++did_work;
#endif
	    if (did_work == 0 && (option_flag&OPTION_NOKBD) == 0)
		 DOSSLEEP((long)5);

skip_kbd_mouse:

	    /* check for ub operations */
	    check_ub();

	    send_pending_interrupts(); 

#ifdef PROFILING
	     pt_main_cnt++;
	     tmp_time = elapsed_time();
	     pt_main_time += tmp_time - pdi_last_time;
	     pdi_last_time = tmp_time;
#endif
	}	/* end while */
}


next_request(op)
{
	int	spk_id;

	switch ( op ) {

	case CB_REBOOT:
			/*
			 * Note to Bill: please rearrage these to be the
			 * same as the dos versions...
			 */
			if (cpu_number == 0) {
				int i;
				/*
				 * this code kills off the other sessions
				 */
				for (i=1; i < ncpu; i++) {
			       		DOSSTOPSESSION(0,&sid[i],0);
				}
			}
			reboot();	/* shouldn't return */
			return 0;
	case CB_HALT:	
			if (cpu_number == 0) {
				int i;
				/*
				 * this code kills off the other sessions
				 */
				for (i=1; i < ncpu; i++) {
			       		DOSSTOPSESSION(0,&sid[i],0);
				}
			}
			return 0;
	case CB_RESTART:
			/*
			 * Note to Bill: please rearrage these to be the
			 * same as the dos versions...
			 */
			if (cpu_number == 0) {
				int i;
				/*
				 * this code kills off the other sessions.
				 */
				for (i=1; i < ncpu; i++) {
			       		DOSSTOPSESSION(0,sid[i],0);
				}
			}
			reboot();	/* shouldn't return */
			return 0;
	case CB_IPL:
	case CB_POR:
			/*
			 * Bill needs to fix this I can't follow
			 * the dos version...
			 *
			 * to start an other cpu from the master:
			 *
			 * DOSFLAGPROCESS(pid[CPU_TO_IPL],0 {Flag A},0{arg});
			 */
			break;
	case CB_QEOI:
	case CB_EOI3:
	case CB_EOI4:
	case CB_AFIREQ:
#ifdef OS2_DEBUG
			printf("non-supported CB op %x\n",op);
#endif
			break;
	case CB_TODRESET:
#ifdef OS2_DEBUG
			printf("CB_TODRESET request\n");
#endif
			break;
	case CB_MASKREQ:
			maskint(CBCB_UNIX_ADDR(MASKENT));
			break;
	case CB_TODREAD:
			getclock();
			break;

	case CB_HDREQ:
			initiate_hard_disk_op(CBCB_UNIX_ADDR(HDENT));
			break;
	case CB_FDREQ:
		       initiate_floppy_disk_op(CBCB_UNIX_ADDR(FDENT));
		       break;
	case CB_MCREQ:
			mcreq(CBCB_UNIX_ADDR(MCENT));
			break;
	case CB_UBREQ:
			ubstrategy(CBCB_UNIX_ADDR(UBENT));
			break;
	case CB_BIOSREQ:
			process_vga_command();
			break;
	case CB_MSREQ:
			mouse_command(CBCB_UNIX_ADDR(MSENT)); 
			break;
	case CB_KBREQ:
			kbdcmd();
			break;

	case CB_SPKREQ:
			DOSCREATETHREAD(speaker,(int far *) &spk_id,
				build_c_args((char far *)spk_stack,
					SPEAKER_SIZE,2,
					CBCB_UNIX_ADDR(SPKENT)
				)
			);
			break;
	case CB_TAPEREQ:	/* no tape for OS/2 */
			break;

	case CB_OPREQ:	/* no optical disk for OS/2 */
#ifdef notdef
			initiate_op_disk_op(CBCB_UNIX_ADDR(OPENT));
#endif
			break;
	case CB_PUTC:
			putchar((char) CBCB_UNIX_ADDR(PUTCENT));
			break;

	default:
			printf("undefined cbcb opcode: %x\n",
				op);
			break;
	}
	
	/* clear the opcode field */
	cbcbptr->op_code = 0;

	return(1);		/* normal return */
}

#ifdef OS2_DEBUG

print_cbcb(cbcb)
struct cbcb	*cbcb;
{	int i;

	printf("printing cbcb at 0x%04x\n",cbcb);
	printf("\top_code:\t0x%04x\n",cbcb->op_code);
	printf("\tromp_int3:\t0x%x\n",(u_short) cbcb->romp_int3);
	printf("\tromp_int4:\t0x%x\n",(u_short) cbcb->romp_int4);
	for(i=0; i < MAXCBCBENT ; i++ )
		printf("\tcbcb_ent[%x]:\t0x%08lx\t0x%08lx\n",i,
			cbcb->cbcb_ent[i].unix_cb,
			cbcb->cbcb_ent[i].pc_cb);
	printf("\tdummy1:\t0x%x\n",(u_short)cbcb->dummy1);
	printf("\tdummy2:\t0x%x\n",(u_short)cbcb->dummy2);
	printf("\tdebug:\t0x%04x\n",cbcb->debug);
	printf("\tdummy3:\t0x%x\n",(u_short)cbcb->dummy3);
	printf("\tromp_int0:\t0x%x\n",(u_short)cbcb->romp_int0);
}
#endif

void
unix_cleanup()
{
	reset_lan_devices();
	reset_ub_devices();
	mcclose();		/* close shared mc device */
	rompinit();
	close_hard_disks();
	pcif_unlock(pcif_data_handle);
	DOSCLOSE(pcif_handle);	/* don't care about errors now ! */
	close_mouse();
	restore_screen_for_exit();
#ifdef UB_DEBUG
	print_fci_count();
#endif
#ifdef PROFILING
	print_p_info();
#endif
}

void far
shutdown()

{
	unix_cleanup();
	DOSEXITLIST(3,0);
}

reboot()
{
	long	return_codes;
	char	buffer[256];

	VIOSAVREDRAWUNDO(1,1,0);	/* kill the save/redraw thread */
	unix_cleanup();
	DOSEXITLIST(2,shutdown); /* remove shutdown from the exit list */
	if (DOSEXECPGM((char far *)buffer,256,1,(char far *)unix_name,
					(char far *)0,(long far *)&return_codes,
					(char far *) unix_path) ) {
		printf("DosExecPgm failed due to %s\n",buffer);
		printf("couldn't exec %s\n", unix_path);
	}
	exit(0);
}

void far
proc_start(sig_number,arg)
	int sig_number,arg;
{
	if (not_started) {
		not_started = 0;
		DOSRESUMETHREAD(sleep_tid);
	}
}
	


/*
 * 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) {
#ifndef OS2_CODE
			delay(delay_time);
#endif
			if (kbhit())
				break;
		}
}

#define INDICATED_THREAD 2
static pthread(name,id)
char *name;
int id;
{
	unsigned int prio;

	DOSGETPRTY(INDICATED_THREAD, (unsigned far *) &prio, id);
	printf("priority of thread %d (%s) = %x\n", id, name, prio);
}

#ifdef DEBUG
static initdebug()
{
	struct ProcIDsArea pids;
	DOSGETPID((struct ProcIDsArea far *) &pids);
	pthread("main", pids.procid_ctid);		/* current process' thread ID */
	pthread("disk", hd_io_thread_id); /* id of the hard disk thread */
	pthread("floppy", fd_io_thread_id); /* id of the floppy disk thread */
	pthread("kbd", kbd_tid);	/* id of the kbd thread */
	pthread("mc", mc_tid);		/* id of the mc thread */
	pthread("clock", clock_tid);	/* id of the clock thread */
#ifdef MAIN_POLL
	pthread("loop", main_tid);	/* id of the main thread */
#endif
#ifndef POLL_MOUSE
	pthread("mouse", mouse_tid);	/* id of the mouse thread */
#endif /* POLL_MOUSE */
	pthread("vga", vio_tid);	/* id of the vga save/restore thread */
}
#endif /* DEBUG */


#ifndef MAIN_POLL
/*
 * thread to handle the cbcb op code's and interrupts passed from 
 * the pcif driver.
 */
static void far
main_thread()
{
	int rc;
	int op;

	SET_THREAD_PRIORITY(PRI_MAIN_THREAD);
	for (;;) 
	{
	     if (rc = DOSDEVIOCTL((char far *) 0,(char far *)0,
			    PCIF_WAIT,PCIF_CAT,pcif_handle))
		fatal("DOSDEVIOCTL WAIT failed",rc);
	     if ( cbcbptr->op_code != 0 ) {
	 	op = exchw(cbcbptr->op_code);
		if (next_request(op) == 0) {
			main_flag++;
			DOSSLEEP((long) 999999);
		}
	    }

	    if (nets_found)
             poll_interrupts();

	}
}

/*
 * start the main thread
 */
int
start_main_thread()
{
	char	*stack;
	char	far *thstk;
	int	rc;
	char	*malloc();

	if ((stack = malloc(512)) == 0)
		fatal("No memory for main thread\n");
	stack += 512;
	thstk = (char far *)stack;
	if (rc = DOSCREATETHREAD(main_thread,(u_int far *)&main_tid,thstk))
		fatal("Unable to create main thread",rc);
}
#endif /* MAIN_POLL */

/*
 * update the clock time every 1/2 second 
 */
static void far
clock_thread()
{
	SET_THREAD_PRIORITY(PRI_CLOCK_THREAD);
	for (;;) 
	{
		DOSSLEEP((long) 500);
		getclock();	/* process clock */
	}
}

/*
 * start the clock thread
 */
int
start_clock_thread()
{
	char	*stack;
	char	far *thstk;
	int	rc;
	char	*malloc();

	if ((stack = malloc(512)) == 0)
		fatal("No memory for clock thread\n");
	stack += 512;
	thstk = (char far *)stack;
	if (rc = DOSCREATETHREAD(clock_thread,(u_int far *)&clock_tid,thstk))
		fatal("Unable to create clock thread",rc);
}
