
/* ------------------------------------------------------------------------*/
/* Project:       FEM BOOT PROGRAM                                         */
/* Filename:      FEM.C                                                    */
/* Description:   BOOT PROGRAM FOR THE GENERIC ARCNET FEM BOARD            */
/* Version:       1.01                                                     */
/* Author:        Jack Fried;				                   */
/* History:       ORIGINAL 7/17/97  for com200510                          */
/*		           11/20/97  edited for built in self test 	   */
/*			   2/13/98   edited for active network map	   */
/*			   3/25/98   edited for delfault load after 30 sec */
/*			   3/26/98   user program readback feature added   */
/*			   4/10/98   Node id reversed due to board error   */
/*			   5/06/98   user selectable group ids		   */ 
/*			   5/08/98   clean up and comments	   	   */
/* ------------------------------------------------------------------------*/
/*									   */
/*   									   */
/*   This program is the main boot program for the FEM. It initilizes 	   */
/*   the arcnet core and decodes the packet header information. 	   */
/*   It then runs the appriate function. 				   */
/*									   */
/* ------------------------------------------------------------------------*/
/*
DATE: 7/17/97

       The software on this disk Has NOT been fully tested.  In no way is
   BNL responsible for the use of this software or any damage resulting in its
   use.  If any bugs are found in this software  please send me E-mail 
   stating the problem and solution if you have one.   
						Jack Fried      
						jfried@inst.inst.bnl.gov

*/



#include  <fem.h>
#define   PKT_size      500             /*packet size normal value  500 bytes */
#define   debug        if (1) 



/* --------------------------------------------------------------------------*/
/* external variable definitions                                             */
/* ------------------------------------------------------------------------- */
extern USIGN16 arc51_port;
extern USIGN16 arc51_swport;
extern USIGN8  arc51_nodeid;
extern USIGN16 arc51_port;



/* ------------------------------------------------------------------------- */
/* global variable definition                                                */
/* ------------------------------------------------------------------------- */
USIGN8 buf[512];
USIGN8 userbuf[512];
int nid = 0x15;
int grpid =0;
int temp;


/*****************************************************************************
 *
 * FUNCTION NAME: main
 *
 * DESCRIPTION  : This function calls the arcnet core setup function.
 *
 * RETURN VALUE : NONE
 ******************************************************************************/

void main(void)
{  
   int status,nid1,nid2,m1,m2,i;
   
  GUF[0] = (int) readback;;
  GUF[1] = (int) ERROR;
  GUF[2] = 1;
  GUF[3] = 30;
  GUF[4] = 0;
  GUF[5] = 0;

  HOPDATA[0] = 0x88;
				/*  read in node ID */
     nid1 = (* (char xdata *) 0xfeff);
     m1= 0x80;
     m2= 0x01;
     nid2 = 0;
     for(i=0;i<=7;i++)  /* fix reverse node id */
     {
       if((nid1 & m1) != 0)
          nid2 = nid2 |  m2;
       m1 =  m1 >> 1;
       m2 =  m2 << 1;
     }
     nid = nid2;

     /* nid = 0x8;*/
     grpid = nid & (0xf0);
     arc51_nodeid = nid;
	
   if (nid == 0x02)
   {
      nid = 0x01;
      grpid = nid & (0xf0);
      arc51_nodeid = nid;
      test_fun();
      while(1);
   }
   status = arc51_init();           /* init the arcnet core */
   if(status == E_OK) 
	node_wake();
   else
	 ERROR(0);
   scan_net();
}


/*****************************************************************************
 *
 * FUNCTION NAME: scan_net
 *
 * DESCRIPTION  : This function is in a loop waiting for a packet to arrive.
 *		  When a packet arrives it determines what to do with it and 
 *		  acts accordinly.
 *
 * RETURN VALUE : NONE
 ******************************************************************************/

void scan_net()
{
int i=0;

   while(1)
   {
      if (arc51_read(&buf[0]) == E_OK)        /* if status = 0 a packet was recived */
	if((buf[NODEID+4] == nid) || (buf[GRPCMD+4] == PUB) || (buf[GRPCMD+4] == grpid))
	    if (buf[ERR+4] == 0)
	    {
	      setup_pckt();                /* read in packet header info */
	      if(pckt.cmd == ECHO)
		 cmd_rec();
	      else if (mem_dump() == E_OK)
	           cmd_rec();
	    }
	    else
	      ERROR(2);

      if (GUF[2] == 1)
      {i++;
	if (i==3100)
        {
          GUF[3]--;
          i=0;
	  if(GUF[3] == 0)
	  {
             GUF[2] = 0;
	     GUF[3] = 30;
	     if ((* (char code *) PGB) == 0x77)
	     {
                HOPDATA[CMD] = DEF_RTN;
	        HOPDATA[NODEID] = nid;
		USR_FUNC;
		if (GUF[5] != 0)
 		   grpid = GUF[5];
	     }
	     else
	     {
	       pckt.error = 77;
	       ERROR(9);
	     }
	   }
	}
     }
   }
}


/*****************************************************************************
 *
 * FUNCTION NAME: readback
 *
 * DESCRIPTION  : Used to send data to node 1 it can send up to 1 hopper full of 
 *		  data. The user program calls this function with an absolute 
 *	          memory address.
 *
 * RETURN VALUE : NONE
 ******************************************************************************/


void  readback(USIGN16 dlen)
{
     int count,i,ii;
     count = 0; 
			 /* calculate the number of bytes to send */
     dlen =dlen+8;
     HOPDATA[DLEN] =   (USIGN8) ((0xFF00 & dlen) >> 8);
     HOPDATA[DLEN+1] = (USIGN8) (0x00FF & dlen);
	
     while (dlen > count)
     {
	i= 0;
			/* break up the hopper memory into packets */
	while ((i < PKT_size) && (dlen > count))  
	{
	 userbuf[i] = HOPDATA[count];
	 count++;
	 i++;
	}
	buf[0] = 0x01;
	buf[1] = 0x01;
	buf[2] = 0;
	buf[3] = i;
	if ( i >=256) buf[2] =1;        /* for long packet */
	for(ii=0;ii<i;ii++) buf[4+ii] = userbuf[ii];    
	i = arc51_write(&buf[0]);
     }
}


/*****************************************************************************
 *
 * FUNCTION NAME: pktsize
 *
 * DESCRIPTION  : returns packet size stored in buf
 *
 * RETURN VALUE : packet size
 ******************************************************************************/


int   pktsize()
{  int count=0;
			
	count = buf[3];
	if(buf[2] > 0) count += 256;
	return(count);
}



/*****************************************************************************
 *
 * FUNCTION NAME: mem_dump
 *
 * DESCRIPTION  : This function recives the remaining packets over  arcnet and 
 * 		  stores it in xdata memory (hopper memory). (39K MAX)
 *
 * RETURN VALUE : status
 ******************************************************************************/


int mem_dump()
{  int status;
   unsigned int i,j,count;
 
   temp=0;
   count = pktsize();
   i=0;
   while(temp< count)            /* copy first packet to xdata */
   {      
      HOPDATA[temp] = buf[temp+4];
      i++;
      temp++;
   }

   time_out(1);                   /* setup timers */
   while(temp < pckt.dlen)           /* repeat  till all data recived (i == dlen)*/
   {
      if (time_out(0))           /* check for time out */
      {
	    ERROR(3);
	    return(1);
      }
      status = arc51_read(&buf[0]);      /* check if next packet recived */
      if (status == E_OK)                          /* if data was recived */
      {
	time_out(1);            /* reset counters  */
	count = pktsize()-8;
	j=0;
	while(j<count)                       /* copy packet to xdata memory */
	{
	    HOPDATA[temp] = buf[j+12];
	    i++;
	    j++;
	    temp++;
	}
      }
   }
    if(i != pckt.dlen)
   {
       ERROR(6);
       return(1);
   }
   return(E_OK);
}

/*****************************************************************************
 *
 * FUNCTION NAME: setup_pckt
 *
 * DESCRIPTION  : Copy the paccket header information  into the pckt structure.
 * 		  
 * RETURN VALUE : NONE
 ******************************************************************************/


void setup_pckt()
{
  pckt.cmd = buf[CMD+4];
  pckt.node = buf[NODEID+4];
  pckt.devtype=buf[DEVTYPE+4];
  pckt.grpcmd=buf[GRPCMD+4];
  pckt.dlen= ((buf[DLEN+4] << 8) + buf[DLEN+5]);
  pckt.error=  buf[ERR+4];
  return;
}




/*****************************************************************************
 *
 * FUNCTION NAME: copy_code
 *
 * DESCRIPTION  : This fuction copies the CODE to reprogram the flash memory to 
 * 		  RAM  (CODE -> XDATA) so that swaping memory space is possable.
 * 		  
 * RETURN VALUE : NONE
 ******************************************************************************/


void copy_code(void)
{ int i;
    for (i=0;i<=500;i++)
     (* (char xdata *) (FPROG+i))  = (* (char code *) (FPROG+i));   /* copy code */
}


/*****************************************************************************
 *
 * FUNCTION NAME: cmd_rec
 *
 * DESCRIPTION  : This function directs the program were to go after the hopper 
 *		  memory is transfer into XDATA. IT will either call NEW_CODE and 
 *		  reprogram the flash memory or call UFUN the user function located 
 *		  at an absolute address  or ic can call on of the boot code 
 *		  functions
 * 		  
 * RETURN VALUE : status
 ******************************************************************************/



int  cmd_rec()
{

   USIGN16 arc51_setup_reg;		/* variable decloration */
   USIGN16 arc51_config_reg,i=0;
   USIGN8  arc51_config;
   arc51_config_reg =  6;
   arc51_setup_reg = 7;


   GUF[0] = (int) readback;		/* copy absolute memory locations in to registers*/
   GUF[1] = (int) ERROR;

   switch (pckt.cmd)
   {
       case  NEW_CODE:			/* reprogram the flash */
	     copy_code(); 
	     PRG_ROM;			/* call function */
	     if ((* (char code *) PGB) == 0x77)    /* check for errors */
             {
                 if(((char *) 0x000000) != 0)
                     ERROR(4);
             }
             else
           	ERROR(4);
             HOPDATA[CMD] = 01;
	     HOPDATA[NODEID] = nid;
	     HOPDATA[ERR] =  0;
	     HOPDATA[8]  = 0x77;
	     readback(1);     		/* reply to node 1  status */
       	     break;
       case  RESET_SW:			/* software reset */
	     SW_RESET;
	     break;
       case  RESPOND:			/* have node respond to check it is alive */
	     HOPDATA[CMD] = RESPOND;
	     HOPDATA[NODEID] = nid;	/* set up values to send back */
	     HOPDATA[ERR] =  0;
	     readback(0);     		
	     break;
       case  ECHO:			/* echos back the hopper memory */
	     HOPDATA[CMD] = ECHO;
	     HOPDATA[NODEID] = nid;
	     HOPDATA[ERR] = 0;
	     readback(pckt.dlen);
	     break;
       case  TOKEN_OFF:			/* turns off the token */
					/* turns off transmiter and turns on recive all */
	     arc51_config = (USIGN8)INP(arc51_config_reg);
             arc51_config = (USIGN8)(((arc51_config & SUB_AD) | SETUP_REG) & ~TXEN);
             OUTP(arc51_config_reg, arc51_config);
             OUTP(arc51_setup_reg, (0X80 | 0x10));
	     break;

       case  TOKEN_ON:			/* wake up node. turn token on */
					/* restart transmiter and turn off recive all */

	     arc51_config = (USIGN8)INP(arc51_config_reg);
             arc51_config = (USIGN8)(arc51_config | TXEN);
             OUTP(arc51_config_reg, arc51_config);
             OUTP(arc51_setup_reg, 0X80);
	     break;	
       case  NET_MAP:			/* acive memory map */
	     HOPDATA[CMD] = NET_MAP;	/*node responds with its node id */
	     HOPDATA[NODEID] = nid;
	     HOPDATA[ERR] =  0;
	     readback(0);     
	     break;
       case  FEM_VER:			/* boot code version number */
	     HOPDATA[CMD] = NET_MAP;
	     HOPDATA[NODEID] = nid;
	     HOPDATA[ERR] =  0;
	     HOPDATA[8] = 2;
	     readback(1);     
	     break;
       case  GET_PRG:			/*read back user program form flash */
	     HOPDATA[NODEID] = nid;
	     HOPDATA[ERR] =  0;
	     for(i=0;i<=(0x9c00);i++)	
	       HOPDATA[8+i] = (* (char code *) (0x4000+i));
	     readback(0x9c00);     
	     break;
       default :			/* call user function */

	     if ((* (char code *) PGB) == 0x77)	/* check to see if there is a user program */
	     {
		USR_FUNC;
		if (GUF[5] != 0)	/* set up group id if necesary */
 		   grpid = GUF[5];
	     }
	     else
	     {
	       pckt.error = 77;
	       ERROR(5);
	       return(1);
	      }
   }
    return(0);
}



/*****************************************************************************
 *
 * FUNCTION NAME: ERROR
 *
 * DESCRIPTION  : This function controls the responce of an error.
 *		  if an error type is 0 this is a critical error such as network 
 *	          not working. this would cause the FEM to reset
 *		  if an error type is 1-255 this is a software error and  information 
 *		  is sent back over the network. the number is the error code.
 * 		  
 * RETURN VALUE : NONE
 ******************************************************************************/


void ERROR(int type)
{ 
     if(type ==0)
     {
	     FNP = &SWRESET;	/* generate a reset */
	     fblock = FNP;
	     (*fblock) ();
     }
     else
     {
	HOPDATA[CMD] = 250;
	HOPDATA[NODEID] = nid;
	HOPDATA[ERR] = type;
	readback(0);  
   	HOPDATA[ERR] = 0; 
     }
}


/*****************************************************************************
 *
 * FUNCTION NAME: node_wake
 *
 * DESCRIPTION  : This is sent over the network to the control node stating 
 *		  this node was just reset due to a power up or  a critical error.
 * 		  
 * RETURN VALUE : NONE
 ******************************************************************************/


void node_wake()
{
	
           HOPDATA[CMD] = 251;
	   HOPDATA[NODEID] = arc51_nodeid;
	if(nid == arc51_nodeid)
	   HOPDATA[ERR] =  1;
	else
	   HOPDATA[ERR] =  7;	/* duplicate node id detected */
	   readback(0); 
	HOPDATA[ERR] = 0;    
}



/*****************************************************************************
 *
 * FUNCTION NAME: time_out
 *
 * DESCRIPTION  : this function conts to see if there is a time out;
 *		  about 5 sec, (3 counters);
 *
 * VARIABLE : 	x = 0:  inc counters and check for time out return 1 if time out
 *		x = 1:  reset counters for a new run
 * 		  
 * RETURN VALUE : time out
 ******************************************************************************/


int time_out(int x)
{
 static int c1,c2,c3;

switch(x)
{

     case   0:          /* count mode */
		c1++;                   /*   counters */
		if (c1 == 10000)
		{
		   c2++;
		   c1 = 0;
		}
		if (c2 == 2)
		{
		   c3++;
		   c2=0;
		}
		if (c3 == 1)
			return(1);
		break;
    case  1:            /* reset counters */
		c1=0;
		c2=0;
		c3=0;
		break;
}
return(0);
}


/*****************************************************************************
 *
 * FUNCTION NAME: test_fun
 *
 * DESCRIPTION  : this function is used with the arcnet test board to test most of
 * 		  the boards function.
 * 		  
 * RETURN VALUE : NONE
 ******************************************************************************/


void test_fun()
{

int i,j,k,status,t1=0,t2=0,t3=0;
char look;

/* set up serial port */   
     PCON = 0x80;
     TH1  = 0xDD; 
     SCON = 0x52;
     TMOD = 0x22;
     TCON = 0x50;
   

 status = arc51_init();           /* init the arcnet core */
   if(status == E_OK) 
   {
	node_wake();
        t1 = 1;
        printf("init ok");
   }
   else printf("error");


/* set register on test card*/
(* (char xdata *) (0xfef9 )) = 0x00;
/* led test */
j=1;
for(i= 0;i<=40;i++)
{
P3.3=1;
if (j > 200 )
   while ( j != 1)
   {
       P3.3=1;
       j=j/2;
       (* (char xdata *) (0xfefa )) = j;
       for(k=0;k<=6000;k++);
       P3.3=0;
   }
   (* (char xdata *) (0xfefa )) = j;
for(k=0;k<=6000;k++);
P3.3=0;
   j=j*2;
}

     printf("\n<--------------------------------->\n");
     printf(  "<             BNL  (BOOT)         >\n");
     printf(  "<            FEM.C  ver 1.00      >\n");
     printf(  "<           TEST ROUTINE          >\n");
     printf(  "<--------------------------------->\n");



printf("\n  NODE 2 test routine starting\n ");



/* set test board reg */
(* (char xdata *) (0xfef9 )) = 0x20;


/*set port write */
   P3.3 = 0;
   P1= 0x77;

/* toggel prr to reset the d-flip flops on test board */
   P3.2 = 1;
   P3.2 = 0;


/*   check the adress lines */
  i =  (* (char xdata *) (0xfefb ));
  (* (char xdata *) (0xfefb )) = i;


printf(" got in the user program\n\n");

/* check port write, data read
   address lines and data lines
*/

i = 0;
    P1 = 0x77;	
    printf(" 0x77--> %x    \n", look = (* (char xdata *) (0xfefa )));
    if (look != 0x77)  i = 1;
    P1 = 0x55;	
    printf(" 0x55 --> %x    \n",  look = (* (char xdata *) (0xfefa )));
    if (look != 0x55)  i = 1;
    P1 = 0x33;	
    printf(" 0x33 --> %x    \n",  look = (* (char xdata *) (0xfefa )));
    if (look != 0x33)  i = 1;
    P1 = 0x44;	
    printf(" 0x44 --> %x    \n", look = (* (char xdata *) (0xfefa )));
    if (look != 0x44)  i = 1;
    P1 = 0xaa;	
    printf(" 0xaa --> %x    \n", look = (* (char xdata *) (0xfefa )));
    if (look != 0xaa)  i = 1;
    P1 = 0x00;	
    printf(" 0x00 --> %x    \n", look = (* (char xdata *) (0xfefa )));
    if (look != 0x00)  i = 1;
    P1 = 0xff;	
    printf(" 0xff --> %x    \n", look = (* (char xdata *) (0xfefa )));
    if (look != 0xff)  i = 1;


if (i != 1)
{
 t3 = 1;
 printf("\n data and port check ok\n");
}

/*
check data line and write
*/

for(j=0;j<=500;j++)
   (* (char xdata *) (0xfefa )) = j;

/*
togal P3.5 for PR 

*/
P3.5 = 0;
P3.5 = 1;   
P3.5 = 0;

/* check each chip select */

for (i=0;i<=20;i++)
    gen_cs(i);

/* check data write compare */
(* (char xdata *) (0xfef9 )) = 0x25;

/*togal port write*/
P3.3=1;
P3.3=0;

testsend(0x03);

while(1)
   {
      if (arc51_read(&buf[0]) == E_OK)        /* if status = 0 a packet was recived */
	if((buf[NODEID+4] == nid) || (buf[GRPCMD+4] == PUB) || (buf[GRPCMD+4] == grpid))
	    if (buf[ERR+4] == 0)
	    {	    	
		testsend(0x03);
		(* (char xdata *) (0xfef9 )) = 0x2f;
		/*togal port write*/
		P3.3=1;
		P3.3=0;
	    }
	    else
	      ERROR(2);
   }

 /* printf("the test is compleate");*/
}





/*****************************************************************************
 *
 * FUNCTION NAME: gen_cs
 *
 * DESCRIPTION  : this function is used to generate chip selects on the arcnet board
 *		  used with the test fucntion
 * 		  
 * RETURN VALUE : NONE
 ******************************************************************************/


void gen_cs(int x)   
{ 

switch (x) {

	case 0: (* (char xdata *) 0xfefd) = 0xfe;   /* chip select 0*/
	        (* (char xdata *) 0xfefd) = 0xff;   /* clear	    */
	        break;
	case 1: (* (char xdata *) 0xfefd) = 0xfd;   /* chip select 1*/
	        (* (char xdata *) 0xfefd) = 0xff;   /* clear	    */
	        break;
	case 2: (* (char xdata *) 0xfefd) = 0xfb;   /* chip select 2*/
	        (* (char xdata *) 0xfefd) = 0xff;   /* clear	    */
	        break;
	case 3: (* (char xdata *) 0xfefd) = 0xf7;   /* chip select 3*/
	        (* (char xdata *) 0xfefd) = 0xff;   /* clear	    */
	        break;
	case 4: (* (char xdata *) 0xfefd) = 0xef;   /* chip select 4*/
	        (* (char xdata *) 0xfefd) = 0xff;   /* clear	    */
	        break;
	case 5: (* (char xdata *) 0xfefd) = 0xdf;   /* chip select 5*/
	        (* (char xdata *) 0xfefd) = 0xff;   /* clear	    */
	        break;
	case 6: (* (char xdata *) 0xfefd) = 0xbf;   /* chip select 6*/
	        (* (char xdata *) 0xfefd) = 0xff;   /* clear	    */
	        break;
	case 7: (* (char xdata *) 0xfefd) = 0x7f;   /* chip select 7*/
	        (* (char xdata *) 0xfefd) = 0xff;   /* clear	    */
	        break;

	case 8: (* (char xdata *) 0xfefe) = 0xfe;   /* chip select 8*/
	        (* (char xdata *) 0xfefe) = 0xff;   /* clear	    */
	        break;
	case 9: (* (char xdata *) 0xfefe) = 0xfd;   /* chip select 9*/
	        (* (char xdata *) 0xfefe) = 0xff;   /* clear	    */
	        break;
	case 10: (* (char xdata *) 0xfefe) = 0xfb;   /* chip select 10*/
	         (* (char xdata *) 0xfefe) = 0xff;   /* clear	    */
	         break;
	case 11: (* (char xdata *) 0xfefe) = 0xf7;   /* chip select 11*/
	         (* (char xdata *) 0xfefe) = 0xff;   /* clear	    */
	         break;
	case 12: (* (char xdata *) 0xfefe) = 0xef;   /* chip select 12*/
	         (* (char xdata *) 0xfefe) = 0xff;   /* clear	    */
	         break;
	case 13: (* (char xdata *) 0xfefe) = 0xdf;   /* chip select 13*/
	         (* (char xdata *) 0xfefe) = 0xff;   /* clear	    */
	         break;
	case 14: (* (char xdata *) 0xfefe) = 0xbf;   /* chip select 14*/
	         (* (char xdata *) 0xfefe) = 0xff;   /* clear	    */
	         break;
	case 15: (* (char xdata *) 0xfefe) = 0x7f;   /* chip select 15*/
	         (* (char xdata *) 0xfefe) = 0xff;   /* clear	    */
	         break;
    }
}



/*****************************************************************************
 *
 * FUNCTION NAME: gen_cs
 *
 * DESCRIPTION  : this function is used to generate a packet set to all nodes 
 *		  to generate a responce from them. used with the test function
 * 		  
 * RETURN VALUE : NONE
 ******************************************************************************/


void  testsend(int x)
{
     int count,i,ii,dlen;
     count = 0; 
			 /* calculate the number of bytes to send */
     dlen =9;
     HOPDATA[DLEN] =   (USIGN8) ((0xFF00 & dlen) >> 8);
     HOPDATA[DLEN+1] = (USIGN8) (0x00FF & dlen);
     HOPDATA[CMD] = (USIGN8) x; /*send command x*/
     HOPDATA[NODEID] = arc51_nodeid;
     HOPDATA[8] = 0x03;		/* send a command 3 (respond) */
     while (dlen > count)
     {
	i= 0;

			/* break up the hopper memory into packets */
	while ((i < PKT_size) && (dlen > count))  
	{
	 userbuf[i] = HOPDATA[count];
	 count++;
	 i++;
	}
	buf[0] = 0x00;
	buf[1] = 0x00;
	buf[2] = 0;
	buf[3] = i;
	if ( i >=256) buf[2] =1;        /* for long packet */
	for(ii=0;ii<i;ii++) buf[4+ii] = userbuf[ii];    
	i = arc51_write(&buf[0]);
     }
}