
/* Copyright William Schelter. All rights reserved.  This file does
the low level relocation which tends to be very system dependent.
It is included by the file sfasl.c
*/

#define EXTERNAL_P(rel) \
  relocation_info.r_type & )
  
#define HI12 0xfff00000
#define LO20 ~HI12
	
foo(){};
relocate()
{
  char *where;

  {unsigned int new_value;
   char tem [10];
#ifdef DEBUG
   printf("\nEnter relocate:*srelocation_info {r_symndx= %d, r_vaddr = %d,:",
	  relocation_info.r_symndx,
	  relocation_info.r_vaddr              
	  );fflush(stdout);
#endif
   where = the_start + relocation_info.r_vaddr;

   if(relocation_info.r_symndx < S_BSS){
#ifdef DEBUG
     printf("(relocation_info.r_symndx = %d < S_BSS)",relocation_info.r_symndx
	    );fflush(stdout);
     print_name(&symbol_table[relocation_info.r_symndx]);
#endif     
     switch(relocation_info.r_type){
     case R_KCALL:
       /* instructions like balix take a 20 bit argument
	  which wants to be the displacement in half words to
	  from the address of the instruction to the actual
	  address. */
       {int displ;
	unsigned int new;
	displ=  symbol_table[relocation_info.r_symndx].n_value -
	  (int)where;
	new= *(unsigned int *)where;

	/*			*(unsigned int *)where
				= (new & HI12) | ((displ >> 1) & LO20); */
	  /* need to store the halves separately, because word pointers
	     must be aligned */
	((unsigned short *)where)[0]=0x8b00;
	((unsigned short *)where)[1]=0x0c00;
	return ;}
     case R_PCRBYTE:		/* byte (pc relative) */
     case R_PCRWORD:		/* word (pc relative) */
     case R_PCRLONG:		/* word (pc relative) */       
       new_value=   - (int)start_address
	 + symbol_table[relocation_info.r_symndx].n_value;
       break;
     default:
       { new_value= 
	   symbol_table[relocation_info.r_symndx].n_value;}}}
   else
     { switch(relocation_info.r_symndx){
     case S_DATA: case S_BSS: case S_TEXT:
       new_value= (int)start_address;
       break;
     default:
       dprintf(relocation_info.r_type = %d, relocation_info.r_type);
#ifdef DEBUG       
       printf("\nrelocation_info {r_symndx= %d, r_vaddr = %d, Ignored:",
	      relocation_info.r_symndx,
	      relocation_info.r_vaddr              
	      );fflush(stdout);
#endif
       goto DONT;}
     };
   dprintf((type %d),relocation_info.r_type);
   switch(relocation_info.r_type){
   case R_RELBYTE:
   case R_PCRBYTE:     
     *( char *)where = new_value + *( char *) where;
     break;
   case R_RELWORD:
   case R_PCRWORD:     
     *( short *)where = new_value + *( short *) where;
     break;
   case R_RELLONG:
   case R_PCRLONG:
     /* I guess it must be long if in these areas
	I don't see how the size can vary.
      */
      if (((int)where %4) !=0) FEerror("long alignment not long aligned",0,0);
     *( long *)where = new_value + *( long *) where;
     break;
   default:
     printf("(bad type %d)",relocation_info.r_type);
   }
 DONT:;
 }
}

typedef int (*FUNC)();



/*  #define describe_sym(n) do{if (debug){printf("Sym No %d:",n); print_name(symbol_table+ (n));}}while(0)
*/

/* #include "spadutils.c" */


