/*
 * 5 dining philosophers: a sample rtx demo for gcc
 *	simulation of the classic dining philosophers problem.
 *
 *	++jrb
 *	bang:   {any internet host}!dsrgsun.ces.CWRU.edu!bammi
 *	domain: bammi@dsrgsun.ces.CWRU.edu
 *	GEnie:	J.Bammi
 *	CIS:	71515,155
 */
#include "grtxbind.h"

#ifndef NULL
#define NULL ((void *)0)
#endif

#define fflush(x)	/* nothing */
#define printf(x)	Bconws(x)
#define fprintf(s,x)	Bconws(x)
#define clear()		printf("\033E")
#define moveto(r,c)	printf("\033Y"), Bconout(2,r+040),Bconout(2,c+040)
#define Rand()		(Random() >> 8)
void Bconws();

typedef struct {
    char	*name;		/* process name    */
    char	*pid;		/* pid of process  */
    short	event;		/* event (ready) condition */
    int		me;		/* my index		   */
    int		left;		/* index of phil to left   */
    int		right;		/* index of phil to right  */
    int		col;		/* column for printing     */
    long	message[4];	/* message buffer	   */
    short	eventbuf;	/* event buffer		   */
} PHIL;

/* 5 philosophers on a round table */
PHIL phils[] = {
 	{"phil-0", (char *)NULL, 0x001, 0, 4, 1, 0,  0L, 0L, 0L, 0L, 0 },
 	{"phil-1", (char *)NULL, 0x002, 1, 0, 2, 16, 0L, 0L, 0L, 0L, 0 },
 	{"phil-2", (char *)NULL, 0x004, 2, 1, 3, 32, 0L, 0L, 0L, 0L, 0 },
 	{"phil-3", (char *)NULL, 0x008, 3, 2, 4, 48, 0L, 0L, 0L, 0L, 0 },
 	{"phil-4", (char *)NULL, 0x010, 4, 3, 0, 64, 0L, 0L, 0L, 0L, 0 }
};

/* forks: number of forks available to philosopher i
 * note: mutex access at all times (ie only one process may read/write
 * any element of forks at one time, implemented via semaphore).
 * usage:
 * at any time forks[i] can only be 0, 1, or 2
 * when a philosopher [i] wants to eat, forks[i] must be 2,
 * if it is
 *	decrement forks to the right and left by one and start eating
 * otherwise
 *	wait for event phil[i].event (ready condition for phil i) to occur.
 *
 * when a philosopher is done eating,
 *	increments forks to left and right
 *	if forks[left] == 2	cause event phil[left].event
 *	if forks[right] == 2	cause event phil[right].event
 */
int forks [] = { 2, 2, 2, 2, 2 };

/* fork semaphore (queue) */
char *forkSemaphore;

/* P/V on fork semaphore */
#define Pfork(q)	q_req (forkSemaphore, q, 0, 0L)
#define Vfork(q)	q_send(forkSemaphore, q)

/* screen semaphore, for exclusive access to write to screen */
char *screenSemaphore;

/* P/V on screen semaphore */
#define Pscreen(q)	q_req (screenSemaphore, q, 0, 0L)
#define Vscreen(q)	q_send(screenSemaphore, q)


/*
 * pickup
 *	pickup both forks or wait until both avail
 */
void pickup(phil)
PHIL *phil;	/* philosopher requesting forks */
{
    Pfork(phil->message);	/* mutually exclusive access to forks */
    if(forks[phil->me] != 2)
    {
	Vfork(phil->message);	/* release */
	phil->eventbuf = phil->event;
	e_wait(&phil->eventbuf, 0, 0L); /* wait for ready condition */
	Pfork(phil->message);
    }
    /* get the two forks */
    forks[phil->left]--;	
    forks[phil->right]--;
    Vfork(phil->message);
}

/*
 * putdown
 *	done eating, put down forks,and signal any ready conditions
 */
void putdown(phil)
PHIL *phil;
{
    
    Pfork(phil->message);

    /* drop forks and signal any events */
    if(++forks[phil->left] == 2)
	e_signal(phils[phil->left].pid, phils[phil->left].event);
    if(++forks[phil->right] == 2)
	e_signal(phils[phil->right].pid, phils[phil->right].event);

    Vfork(phil->message);
}

/*
 * philosopher process
 */
void philosopher(phil)
PHIL *phil;
{
    
    Pscreen(phil->message);
    moveto(3, phil->col);
    printf(phil->name);  fflush(stdout);
    Vscreen(phil->message);
    
    while(1)
    {
	Pscreen(phil->message);
	moveto(4, phil->col); printf("  Hungry    "); fflush(stdout);
	Vscreen(phil->message);
	pickup(phil);
	/* eat */
	Pscreen(phil->message);
	moveto(4, phil->col); printf("  Eating    "); fflush(stdout);
	Vscreen(phil->message);
	p_pause( (long)(((Rand() & 15) + 1) * 1000L) );
	putdown(phil);
	Pscreen(phil->message);
	moveto(4, phil->col); printf("  Thinking  "); fflush(stdout);
	Vscreen(phil->message);
	p_pause( (long)(((Rand() & 15) + 1) * 1000L) );
    }
    /*NOTREACHED*/
}

/*
 * kill all live philosophers
 * free fork semaphore queue
 */
void killall()
{
    int i;
    for(i = 0; i < 5; i++)
    {
	if(phils[i].pid != NULL)
	{
	    p_delete(phils[i].pid);
	    moveto(4, phils[i].col);
	    printf("  KILLED!   "); fflush(stdout);
	}
    }
    q_delete(forkSemaphore);
    q_delete(screenSemaphore);
}

/*
 * print a int (short range only)
 */
void printi(val)
register int val;
{
        register int j;
        register int div_idx;
        register int first;
        static int divisors[] = { 10000, 1000, 100, 10, 1 };
	
	/* Special Cases */
        if (val == 0)
	{
		Bconout(2, '0');
		return;
	}
        else if (val == -32768)
	{
                Bconws("-32768");
		return;
	}
	
	/* Get digit for each power of 10 and print them, skip leading 0's */
        first = div_idx  = 0;
	
        if (val < 0)
	{
                Bconout(2, '-');
                val = -val;
	}
        while( div_idx < 5 )
	{
                if(((j = val / divisors[div_idx]) != 0) || first != 0)
		{
                        Bconout(2, j + '0');
                        first = 1;
		}
                val %= divisors[div_idx++];
	}
	
}

/*
 * the top level process, called after rtx initialization
 */
void Top()
{
    int i;
    long message[4];
    PHIL *stk;
    
    /* create semaphores */
    forkSemaphore = q_create("FORKSEM", 0);
    screenSemaphore = q_create("SCRSEM", 0);
    
    /* lower my prio (phils will be given higher prio) */
    p_priority((char *)NULL, -5);
    /* create the philosophers */
    for(i = 0; i < 5; i++)
    {
	stk = &phils[i];
	phils[i].pid = p_create(phils[i].name, p_priority((char *)NULL, 0)+1,
				p_slice((char *)NULL, 0), philosopher,
				(short)(sizeof(PHIL *)), &stk, 512L);
	if(phils[i].pid == (char *)NULL)
	{
	    fprintf(stderr,"P_create failed\r\n");
	    killall();
	    return;
	}
	
    }
    moveto(10, 33); printf("Hit q to quit"); fflush(stdout);
    /* start it all up */
    Vscreen(message);
    Vfork(message);
    
    while((Crawio(0x00ff) & 0x07f) != 'q')
    {
	/* report process status */
	for(i = 0; i < 5; i++)
	{
	    PROCESS_STATE status;
	    
	    p_info(phils[i].pid, &status);
	    Pscreen(message);
	    moveto(7,  phils[i].col);
	    if(status.EWAIT)
		printf(" event wait ");
	    else if(status.QWAIT)
		printf(" queue wait ");
	    else if(status.PAUSED)
		printf(" Paused     ");
	    else if(status.RUNNING)
		printf(" Running    ");
	    else if(status.READY)
		printf(" Ready      ");
	    else
		printf(" Unknown    ");
	    printi(forks[i]);
	    fflush(stdout);
	    Vscreen(message);
	    
	    p_pause(500L);
	}
    }
    killall();
}

void Bconws(str)
char *str;
{
    auto char *s = str;
    
    while(*s)
	Bconout(2, *s++);
}

extern BASEPAGE *_base;

int main()
{
    CONFIG config;
    char *RootPid;
    
    config.basepage = _base;
    config.max_proc = 32;
    config.max_msgs = 64;
    config.max_queues = 64;
    config.create_call = NULL;
    config.delete_call = NULL;
    config.switch_call = NULL;
    
    if((RootPid = rtx_install(&config)) == (char *)NULL)
    {
	fprintf(stderr, "microRtx could not be installed\r\n");
	exit(1);
    }
    clear();
    
    Top();
    rtx_remove();
}
