/************************************************/
/*                                              */
/* File        : calbhard.c                     */
/* Description : CALB hardware base access      */
/*                                              */
/* Authors: G.Cabibbo, A.Passeri                */
/*                                              */
/* Created      : 10.02.1997                    */
/* Last modified: 30.12.1997                    */
/*                                              */
/************************************************/

#include <stdio.h>
#include <Error.h>
#include <Vme.h>
#include <Vme_am.h>
#include <GeoVme.h>
#include "calbhard.h"
#include "calbbits.h"

/*********************************************************************/
/*                    Load calb info map                             */  
/*********************************************************************/

int calbh_loadinfo(char* calb_mapname) 
{
  FILE *calb_map;
  char buff[100];
  int chain,crate,slot,btype,sernum;
  int NumBoards = 0;

  if((calb_map=fopen(calb_mapname,"r"))==NULL) 
  {
    ErrorSetF(CALBH_ERROR_UNKNOWN,"calbmap","calb info file open error");
    return CALBH_ERROR_UNKNOWN;
  } 
  
  for(chain=0;chain<GEOVME_DIM_CHAINS;chain++)
  {
    for(crate=0;crate<GEOVME_DIM_CRATES;crate++)
    {
      for(slot=0;slot<GEOVME_DIM_SLOTS;slot++)
	calb_info[chain][crate][slot]=NULL;
    }
  }
  while(fgets(buff,100,calb_map)!=NULL) 
  {
    if(buff[0]!='%') 
    {
      sscanf(buff,"%d %d %d %d %d ",&chain,&crate,&slot,&btype,&sernum);

      if ((chain<0)||(crate<0)||(slot<0)||
	  (chain>=GEOVME_DIM_CHAINS)||(crate>=GEOVME_DIM_CRATES)||(slot>=GEOVME_DIM_SLOTS))
	{
	  ErrorSetF(CALBH_ERROR_UNKNOWN,"calbmap","chain %i crate %i slot %i error.",chain,crate,slot);
	  return CALBH_ERROR_UNKNOWN;
	}

      if( (calb_info[chain][crate][slot] = 
	   (CALBH_info*) malloc(sizeof(CALBH_info))) == NULL ) 
      {
	ErrorSetF(CALBH_ERROR_UNKNOWN,"calbmap","Calb map allocation error");
	return CALBH_ERROR_UNKNOWN;
      }
      
      calb_info[chain][crate][slot]->btype = btype;
      calb_info[chain][crate][slot]->sernum = sernum;

      NumBoards++;
  
    }
  }
  
  return NumBoards;
}

int calbh_unloadinfo(void) 
{
  int chain,crate,slot;
  
  for(chain=GEOVME_INF_CHAINS;chain<GEOVME_SUP_CHAINS;chain++)
  {
    for(crate=GEOVME_INF_CRATES;crate<GEOVME_SUP_CRATES;crate++)
    {
      for(slot=GEOVME_INF_SLOTS;slot<GEOVME_SUP_SLOTS;slot++)
	free(calb_info[chain][crate][slot]);
    }
  }

  return CALBH_ERROR_OK;
}

/*********************************************************************/
/*                    info query                                     */
/*********************************************************************/

int calbh_sernum(int chain, /* IN */
                  int crate, /* IN */
                  int slot  ) /* IN */
{
  if(calb_info[chain][crate][slot]==NULL)
  {
    printf("\n    calb sernum not available: calb info file not currently opened\n");  
    return CALBH_ERROR_UNKNOWN;
  } 
  else
  { 
    return calb_info[chain][crate][slot]->sernum;
  }
}

int calbh_btype(int chain, /* IN */
                  int crate, /* IN */
                  int slot  ) /* IN */
{
  if(calb_info[chain][crate][slot]==NULL)
  {
    printf("\n    calb btype not available: calb info file not currently opened\n");  
    return CALBH_ERROR_UNKNOWN;
  } 
  else
  { 
    return calb_info[chain][crate][slot]->btype;
  }
}


/*********************************************************************/
/*                          Query routines                           */
/*********************************************************************/

/* returns 0 if a CALB board is not present, 1 else */
int calbh_ispresent(int chain,                   /* IN, VIC chain */
		    int crate,                   /* IN, position in the VIC chain  */
                    int slot)                    /* IN, position in the crate */ 
{
 return ( (GeoVme_exist(chain,crate,slot)==GEOVME_BOARD_EXIST) &&
	  ( (GeoVme_btype(chain,crate,slot)==GEOVME_CADC_BTYPE) ||
	    (GeoVme_btype(chain,crate,slot)==GEOVME_CTDC_BTYPE)   ) );
}

/*********************************************************************/
/*                       Initialization routines                     */
/*********************************************************************/

int calbh_open(int chain,                    /* IN  */
	       int crate,                    /* IN  */
	       int slot,                     /* IN  */
     	       CALBH_id *calb_id)            /* OUT */ 
{
  GeoBoardAddr Calb_Addr;

  *calb_id = (CALBH_id) malloc(sizeof(CALBH_id_base));
  if (*calb_id==NULL)
   {
     ErrorSet(CALBH_ERROR_UNKNOWN,"calbh_open","Malloc returned NULL.");
     return CALBH_ERROR_UNKNOWN;
   }

 if(GeoVmeOpen(chain,crate,slot,&Calb_Addr)!=GEOVME_ERROR_OK) 
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_open_tmp","GeoVmeOpen error: %s",ErrorGetMessage());
     return CALBH_ERROR_UNKNOWN;
   }

 if ((Calb_Addr.btype!=GEOVME_CADC_BTYPE)&&(Calb_Addr.btype!=GEOVME_CTDC_BTYPE))
   {
     GeoVmeClose(chain,crate,slot);
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_open","Chain %i Crate %i Slot %i does not have a CalBoard!",chain,crate,slot);
     return CALBH_ERROR_UNKNOWN;
   }

  if(calb_info[chain][crate][slot]==NULL)
 {
   printf(" calbh_open WARNING: calb info file not previously opened !\n");
   printf("  Serial number not available for board in chain %d  crate %d  slot %d\n",chain,crate,slot);
 }
 else
 {
   if ( calb_info[chain][crate][slot]->btype!=Calb_Addr.btype )
   {  
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_open","Inconsistecy found between calb_info_file and GeoVme_map for Chain %i Crate %i Slot %i !",chain,crate,slot); 
     GeoVmeClose(chain,crate,slot);
     return CALBH_ERROR_UNKNOWN;
   }
 }  

 (*calb_id)->chain = chain;
 (*calb_id)->crate = crate;
 (*calb_id)->slot = slot;
 (*calb_id)->cid = Calb_Addr.cid;
 (*calb_id)->map_ptr = Calb_Addr.map_ptr;
 (*calb_id)->vme_addr = Calb_Addr.addr;  
 (*calb_id)->offs = Calb_Addr.offs;
 (*calb_id)->vme_am = Calb_Addr.am;
 (*calb_id)->btype = -1; 
 (*calb_id)->sernum = -1;


  if(calb_info[chain][crate][slot]==NULL)
 {
   printf(" calbh_open WARNING: calb info file not previously opened !\n");
   printf("  Serial number not available for board in chain %d  crate %d  slot %d\n",chain,crate,slot);
 }
 else
 {
   if ( calb_info[chain][crate][slot]->btype!=Calb_Addr.btype )
   {  
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_open","Inconsistecy found between calb_info_file and GeoVme_map for Chain %i Crate %i Slot %i !",chain,crate,slot); 
     GeoVmeClose(chain,crate,slot);
     return CALBH_ERROR_UNKNOWN;
   }
 else
  {
    (*calb_id)->btype = calb_info[chain][crate][slot]->btype;
    (*calb_id)->sernum = calb_info[chain][crate][slot]->sernum;
  }
 }  

#ifdef DEBUG
 printf("map: %x\naddr: %x\noffs: %x\n",(*calb_id)->map_ptr,
	                               (*calb_id)->vme_addr,
	                               (*calb_id)->offs);
#endif

 VmeSetProperty((*calb_id)->cid,Vme_SET_COPY_FIFO,1);

 return CALBH_ERROR_OK;
}

int calbh_open_raw(unsigned int vme_addr,       /* IN  */
		   int vme_size,                /* IN  */
		   int vme_am,                  /* IN  */
		   int offs,                    /* IN, CALB board offset  */
		   CALBH_id *calb_id)           /* OUT */
{
 *calb_id = (CALBH_id) malloc(sizeof(CALBH_id_base));
  if (*calb_id==NULL)
   {
     ErrorSet(CALBH_ERROR_UNKNOWN,"calbh_open","Malloc returned NULL.");
     return CALBH_ERROR_UNKNOWN;
   }

 (*calb_id)->cid = VmeOpenChannel("Calb","pio");
 if ((*calb_id)->cid==Vme_FAILURE)
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_open","VmeOpenChannel error: %s",ErrorGetMessage());
     return CALBH_ERROR_UNKNOWN;
   }
 (*calb_id)->map_ptr=VmeMapAddress((*calb_id)->cid,vme_addr,vme_size,vme_am);
 (*calb_id)->vme_addr = vme_addr;
 (*calb_id)->vme_am = vme_am;
 (*calb_id)->offs = offs;
 (*calb_id)->chain = -1;  /* set to an invalid value */
 (*calb_id)->crate = -1;  /* set to an invalid value */
 (*calb_id)->slot  = -1;  /* set to an invalid value */

 if ((*calb_id)->map_ptr==NULL)
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_open","VmeMapAddress error: %s",ErrorGetMessage());
     return CALBH_ERROR_UNKNOWN;
   }

#ifdef DEBUG
 printf("map: %x\naddr: %p\noffs: %x\n",
	(*calb_id)->map_ptr,
	(*calb_id)->vme_addr,
	(*calb_id)->offs);
#endif

 VmeSetProperty((*calb_id)->cid,Vme_SET_COPY_FIFO,1);

 return CALBH_ERROR_OK;
}


int calbh_close(CALBH_id calb_id)            /* IN */
{
 int geoc;

 if ((calb_id->chain>=0)&&(calb_id->crate>=0)&&(calb_id->slot>=0))
 { /* normal opened */
   geoc=GeoVmeClose(calb_id->chain,calb_id->crate,calb_id->slot);
   free(calb_id);

   if(geoc!=GEOVME_ERROR_OK) 
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_close","VmeCloseChannel error: %s",
         ErrorGetMessage());
     return CALBH_ERROR_UNKNOWN;
   }

   return CALBH_ERROR_OK;
 }
 else
 { /* raw opened */
   geoc = VmeCloseChannel(calb_id->cid); 
   free(calb_id);
   if (geoc!=Vme_SUCCESS)
   {
  ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_close","VmeCloseChannel error: %s",ErrorGetMessage());
      return CALBH_ERROR_UNKNOWN;
   } 
   else
     return CALBH_ERROR_OK;
 }

}


/*********************************************************************/
/*                       read routines                               */
/*                       return the read value                       */
/*********************************************************************/

unsigned short calbh_read_reg16(CALBH_id calb_id,     /* IN  */
				unsigned int offset)  /* IN  */
{
 unsigned short reg16;
 unsigned short *aptr;

 aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);

#ifdef DEBUG
 if (offset<0x4000)
   printf("read addr: %x \n",aptr);
#endif

/*  Vme_D16READ(calb_id->vme_addr,aptr,reg16);  */
  Vme_D16READ(calb_id->map_ptr,aptr,reg16);

 return reg16;
}

unsigned int   calbh_read_reg32(CALBH_id calb_id,     /* IN  */
				unsigned int offset)  /* IN  */
{
 unsigned int reg32;
 unsigned int *aptr;

 aptr = (unsigned int *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);

#ifdef DEBUG
 if (offset<0x4000)
   printf("read addr: %x \n",aptr);
#endif

 Vme_D32READ(calb_id->map_ptr,aptr,reg32);

 return reg32;
}

/*********************************************************************/
/*                       write routines                              */
/*********************************************************************/

int calbh_write_reg16(CALBH_id calb_id,       /* IN */
		      unsigned int offset,    /* IN */
		      unsigned short reg,     /* IN */
		      unsigned short testmask)/* IN, test if >0 */
{
 unsigned short *aptr;

 aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);

#ifdef DEBUG
 if (offset<0x4000)
   printf("write addr: %x \n",aptr);
#endif

/* Vme_D16WRITE(calb_id->vme_addr,aptr,reg);  */
   Vme_D16WRITE(calb_id->map_ptr,aptr,reg);

 if (testmask!=0)
   {
     unsigned short testreg;

 /*    Vme_D16READ(calb_id->vme_addr,aptr,testreg); */
    Vme_D16READ(calb_id->map_ptr,aptr,testreg);

     if ((testreg&testmask)!=(reg&testmask))
       return CALBH_ERROR_TEST;
   }

 return CALBH_ERROR_OK;
}


/*********************************************************************/
/*                     readLatches routines                          */
/*********************************************************************/

int calbh_readlatch_i_chan(CALBH_id calb_id /* IN  */,       
                            /* OUT */ unsigned int  chan)
{
 unsigned short latch;
 unsigned short *aptr;
 unsigned int offset;

 if ( chan>29 )
   {
     ErrorSetF(CALBH_ERROR_OUTRANGE,"calbh_readlatch_i_chan","Error: channel number %d out of range",chan);
     return CALBH_ERROR_OUTRANGE;
   }
 offset = CALBH_PAGE_LATCH_ofs + chan*2 ;
 aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);

#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif

 Vme_D16READ(calb_id->map_ptr,aptr,latch);

 return latch&0x1fff;
}

int calbh_readlatch_i_all (CALBH_id calb_id /* IN  */,       
                            /* OUT */ CALBH_CHANNELS  latch)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int chan;

 for(chan=0;chan<30;chan++)
   {
      offset = CALBH_PAGE_LATCH_ofs+chan*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
      Vme_D16READ(calb_id->map_ptr,aptr,reg16);
      latch[chan]=reg16&0x1fff;
   }
 return CALBH_ERROR_OK;
}

/*********************************************************************/
/*                     readpage routines                             */
/*********************************************************************/

int calbh_readpage_i_csr(CALBH_id calb_id,                /* IN  */
		         CALBH_CSR_regs *csr)   /* OUT */
{
 csr->creg = calbh_read_creg(calb_id);
 csr->sreg = calbh_read_sreg(calb_id);

 return CALBH_ERROR_OK;
}

int calbh_readpage_i_ped (CALBH_id calb_id /* IN  */,       
                            /* OUT */ CALBH_PEDS_val *peds)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int chan;

 for(chan=0;chan<30;chan++)
   {
 /*  section A peds  */
      offset = CALBH_PAGE_PED_A_ofs+chan*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
      Vme_D16READ(calb_id->map_ptr,aptr,reg16);
      peds->ped_a[chan]=reg16&0xfff;

 /*  section B peds  */
      offset = CALBH_PAGE_PED_B_ofs+chan*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
      Vme_D16READ(calb_id->map_ptr,aptr,reg16);
      peds->ped_b[chan]=reg16&0xfff;
   }
 return CALBH_ERROR_OK;
}

int calbh_readpage_i_zsup(CALBH_id calb_id /* IN  */,   
                            /* OUT */ CALBH_ZSUP_nwind *zwind,    
                            /* OUT */ CALBH_ZSUP_val zsup)
{

 CALBH_CHALF z_nwind;
 CALBH_ZSUP_val_elem zelem;
 int i,j,k;

 calbh_readpage_zsup_elem(calb_id,CALBH_PAGE_ZSUP1_A_ofs,z_nwind,zelem);
 for(i=0;i<15;i++)
 {
   zwind->nwind_a[i]=z_nwind[i];
   for(j=0;j<z_nwind[i];j++)
   {
     zsup[j].zlow_a[i]=zelem[j].zlow[i];
     zsup[j].zhig_a[i]=zelem[j].zhig[i];
   }
   if(z_nwind[i]>1)
   ErrorSetF(CALBH_WARNING_ZSUP,"calbh_readpage_i_zsup",
   "WARNING: there are %d suppressed windows for channel %d slot %d crate %d chain %d\n",
      z_nwind[i],i,calb_id->slot,calb_id->crate,calb_id->chain);
 }

 calbh_readpage_zsup_elem(calb_id,CALBH_PAGE_ZSUP1_B_ofs,z_nwind,zelem);
 for(i=0;i<15;i++)
 {
   zwind->nwind_b[i]=z_nwind[i];
   for(j=0;j<z_nwind[i];j++)
   {
     zsup[j].zlow_b[i]=zelem[j].zlow[i];
     zsup[j].zhig_b[i]=zelem[j].zhig[i];
   }
   if(z_nwind[i]>1)
   ErrorSetF(CALBH_WARNING_ZSUP,"calbh_readpage_i_zsup",
    "WARNING: there are %d suppressed windows for channel %d slot %d crate %d chain %d \n",
      z_nwind[i],i,calb_id->slot,calb_id->crate,calb_id->chain);

 }

 calbh_readpage_zsup_elem(calb_id,CALBH_PAGE_ZSUP2_A_ofs,z_nwind,zelem);
 for(i=0;i<15;i++)
 {
   k=i+15;
   zwind->nwind_a[k]=z_nwind[i];
   for(j=0;j<z_nwind[i];j++)
   {
     zsup[j].zlow_a[k]=zelem[j].zlow[i];
     zsup[j].zhig_a[k]=zelem[j].zhig[i];
   }
   if(z_nwind[i]>1)
   ErrorSetF(CALBH_WARNING_ZSUP,"calbh_readpage_i_zsup",
    "WARNING: there are %d suppressed windows for channel %d, slot %d, crate %d, chain %d \n",
      z_nwind[i],k,calb_id->slot,calb_id->crate,calb_id->chain);
 }

 calbh_readpage_zsup_elem(calb_id,CALBH_PAGE_ZSUP2_B_ofs,z_nwind,zelem);
 for(i=0;i<15;i++)
 {
   k=i+15;
   zwind->nwind_b[k]=z_nwind[i];
   for(j=0;j<z_nwind[i];j++)
   {
     zsup[j].zlow_b[k]=zelem[j].zlow[i];
     zsup[j].zhig_b[k]=zelem[j].zhig[i];
   }
   if(z_nwind[i]>1)
   ErrorSetF(CALBH_WARNING_ZSUP,"calbh_readpage_i_zsup",
    "WARNING: there are %d suppressed windows for channel %d, slot %d, crate %d, chain %d \n",
      z_nwind[i],k,calb_id->slot,calb_id->crate,calb_id->chain);

 }

 return CALBH_ERROR_OK;
}

int calbh_readpage_i_zsup_elem(CALBH_id calb_id /* IN  */,/* IN */ int zofs,
            /* OUT */ CALBH_CHALF z_nwind, /* OUT */ CALBH_ZSUP_val_elem zelem)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int i,j,chan,bit,prev;

 for(chan=0;chan<15;chan++)
   {
   z_nwind[chan] = 0;
   for(j=0;j<CALBH_ZSUP_MAXWIND;j++)
    {
      zelem[j].zlow[chan]=0;
      zelem[j].zhig[chan]=0;
    }
   }
 reg16 = 0xffff;
 for(i=0;i<0x1000;i++)
   {
      offset = zofs+i*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
/*  #ifdef DEBUG
    printf("read addr: %x \n",aptr);
    #endif   
*/
      prev = reg16;
      Vme_D16READ(calb_id->map_ptr,aptr,reg16);
      for(chan=0;chan<15;chan++)
        {
          bit = bits_get_bit(reg16,chan);
          if(bit != bits_get_bit(prev,chan) )
	    {
              if( bit )
		{
                  zelem[z_nwind[chan]-1].zhig[chan] = i-1;
                }
              else
                {
                  zelem[z_nwind[chan]].zlow[chan]=i;
                  z_nwind[chan]++;
                }
            }
        }
   }
/* close last window if upper edge is fff  */
 for(chan=0;chan<15;chan++)
   {
    bit = bits_get_bit(reg16,chan);
    if(bit==0) zelem[z_nwind[chan]-1].zhig[chan]=0xfff;
   }

 return CALBH_ERROR_OK;
}
 
/*********************************************************************/
/*                     writepage routines                             */
/*********************************************************************/

int calbh_writepage_i_ped (CALBH_id calb_id /* IN  */,       
                            /* IN */ CALBH_PEDS_val *peds)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int chan;

 for(chan=0;chan<30;chan++)
   {
 /*  section A peds  */
      if(peds->ped_a[chan] <0)
      {
       fprintf(stderr," \n  Negative ped_a for chain %d crate %d slot %d chan %d !!!\n",
         calb_id->chain,calb_id->crate,calb_id->slot,chan);
       fprintf(stderr,"    value not loaded  !!  \n");
      }
      else
      {
       offset = CALBH_PAGE_PED_A_ofs+chan*2;
       aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
       reg16=peds->ped_a[chan];
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
       Vme_D16WRITE(calb_id->map_ptr,aptr,reg16);
      }
 /*  section B peds  */
      if(peds->ped_b[chan] <0)
      {
       fprintf(stderr," \n  Negative ped_b for chain %d crate %d slot %d chan %d !!!\n",
         calb_id->chain,calb_id->crate,calb_id->slot,chan);
       fprintf(stderr,"    value not loaded  !!  \n");
      }
      else
      {
       offset = CALBH_PAGE_PED_B_ofs+chan*2;
       aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
       reg16=peds->ped_b[chan];
#ifdef DEBUG
 printf("read addr: %x \n",aptr);
#endif
       Vme_D16WRITE(calb_id->map_ptr,aptr,reg16);
      }
   }
 return CALBH_ERROR_OK;
}

int calbh_writepage_i_zsup (CALBH_id calb_id /* IN  */,       
                            /* IN */ CALBH_ZSUP_nwind  *zwind,
                            /* IN */ CALBH_ZSUP_val zsup)
{
 CALBH_CHALF z_nwind;
 CALBH_ZSUP_val_elem zelem;
 int i,j,k;

 /*  protection against negative values */
 for(i=0;i<30;i++)
 {
   if(zwind->nwind_a[i] < 0)
   {
     fprintf(stderr,"\n Negative nwind_a for chain %d crate %d slot %d chan %d !\n",calb_id->chain,calb_id->crate,calb_id->slot,i);
     fprintf(stderr,"   !!! Zerosup values not loaded for this board !!! \n\n");
     return CALBH_ERROR_UNKNOWN;
   }
   else
   {
     for(j=0;j<zwind->nwind_a[i];j++)
     {
       if( zsup[j].zlow_a[i] < 0)
       {
         fprintf(stderr,"\n Negative zsup_low_a for chain %d crate %d slot %d chan %d !\n",calb_id->chain,calb_id->crate,calb_id->slot,i);
         fprintf(stderr,"   !!! Zerosup values not loaded for this board !!! \n\n");
         return CALBH_ERROR_UNKNOWN;
       } 
       if( zsup[j].zhig_a[i] < 0)
       {
         fprintf(stderr,"\n Negative zsup_hig_a for chain %d crate %d slot %d chan %d !\n",calb_id->chain,calb_id->crate,calb_id->slot,i);
         fprintf(stderr,"   !!! Zerosup values not loaded for this board !!! \n\n");
         return CALBH_ERROR_UNKNOWN;
       }
     }
   }

   if(zwind->nwind_b[i] < 0)
   {
     fprintf(stderr,"\n Negative nwind_b for chain %d crate %d slot %d chan %d !\n",calb_id->chain,calb_id->crate,calb_id->slot,i);
     fprintf(stderr,"   !!! Zerosup values not loaded for this board !!! \n\n");
     return CALBH_ERROR_UNKNOWN;
   }
   else
   {
     for(j=0;j<zwind->nwind_b[i];j++)
     {
       if( zsup[j].zlow_b[i] < 0)
       {
         fprintf(stderr,"\n Negative zsup_low_b for chain %d crate %d slot %d chan %d !\n",calb_id->chain,calb_id->crate,calb_id->slot,i);
         fprintf(stderr,"   !!! Zerosup values not loaded for this board !!! \n\n");
         return CALBH_ERROR_UNKNOWN;
       } 
       if( zsup[j].zhig_b[i] < 0)
       {
         fprintf(stderr,"\n Negative zsup_hig_b for chain %d crate %d slot %d chan %d !\n",calb_id->chain,calb_id->crate,calb_id->slot,i);
         fprintf(stderr,"   !!! Zerosup values not loaded for this board !!! \n\n");
         return CALBH_ERROR_UNKNOWN;
       }
     }
   }
 }

 /*  now real bussiness starts !  */
 for(i=0;i<15;i++)
 {
   z_nwind[i]=zwind->nwind_a[i];
   for(j=0;j<z_nwind[i];j++)
   {
     zelem[j].zlow[i]=zsup[j].zlow_a[i];
     zelem[j].zhig[i]=zsup[j].zhig_a[i];
   }
 }
 calbh_writepage_zsup_elem(calb_id,CALBH_PAGE_ZSUP1_A_ofs,z_nwind,zelem);

 for(i=0;i<15;i++)
 {
   z_nwind[i]=zwind->nwind_b[i];
   for(j=0;j<z_nwind[i];j++)
   {
     zelem[j].zlow[i]=zsup[j].zlow_b[i];
     zelem[j].zhig[i]=zsup[j].zhig_b[i];
   }
 }
 calbh_writepage_zsup_elem(calb_id,CALBH_PAGE_ZSUP1_B_ofs,z_nwind,zelem);

 for(i=15;i<30;i++)
 {
   k=i-15;
   z_nwind[k]=zwind->nwind_a[i];
   for(j=0;j<z_nwind[k];j++)
   {
     zelem[j].zlow[k]=zsup[j].zlow_a[i];
     zelem[j].zhig[k]=zsup[j].zhig_a[i];
   }
 }
 calbh_writepage_zsup_elem(calb_id,CALBH_PAGE_ZSUP2_A_ofs,z_nwind,zelem);

 for(i=15;i<30;i++)
 {
   k=i-15;
   z_nwind[k]=zwind->nwind_b[i];
   for(j=0;j<z_nwind[k];j++)
   {
     zelem[j].zlow[k]=zsup[j].zlow_b[i];
     zelem[j].zhig[k]=zsup[j].zhig_b[i];
   }
 }
 calbh_writepage_zsup_elem(calb_id,CALBH_PAGE_ZSUP2_B_ofs,z_nwind,zelem);

 return CALBH_ERROR_OK;
}

int calbh_writepage_i_zsup_elem(CALBH_id calb_id /* IN  */,/* IN */ int zofs,
      /* IN */ CALBH_CHALF z_nwind, /* IN */ CALBH_ZSUP_val_elem  zelem)
{
 unsigned short *aptr;
 unsigned int offset;
 unsigned short reg16;
 int i,j,chan,min,max,lastw;

 min = 0x1000;
 max = 0;
 for(chan=0;chan<15;chan++)
   {
    if(z_nwind[chan]>0)
    { 
     if(zelem[0].zlow[chan]<min) min=zelem[0].zlow[chan];
     lastw = z_nwind[chan]-1;
     if(zelem[lastw].zhig[chan]>max) max=zelem[lastw].zhig[chan];
    }
   }
 for(i=0;i<0x1000;i++)
   {
      reg16 = 0xffff;
      if( (i>=min) && (i<=max) )  
      {
        for(chan=0;chan<15;chan++)
        {
          for(j=0;j<z_nwind[chan];j++) 
          {
            if( (i>=zelem[j].zlow[chan]) && (i<=zelem[j].zhig[chan]) )
             reg16=bits_set_bit(reg16,chan,0);
          }
        }
      }
      offset = zofs+i*2;
      aptr = (unsigned short *) ((unsigned char *)calb_id->map_ptr+calb_id->offs+offset);
/*  #ifdef DEBUG
    printf("read addr: %x \n",aptr);
    #endif   */
      Vme_D16WRITE(calb_id->map_ptr,aptr,reg16);

   }
 return CALBH_ERROR_OK;
}

/*********************************************************************/
/*                      Readfifo routines                            */
/*********************************************************************/

int calbh_read_pio_i_hfifo (CALBH_id calb_id,	/* IN  */
			    unsigned int nels,	/* IN  */
			    unsigned int *buffer,	/* OUT */
			    unsigned int *count)	/* OUT, nr of els really read */
{
  int i,iCount,tempbuf;
  CALBB_HFIFO_bits bits;   
  
#ifdef DEBUG
 printf("readfifo size: %x \n",nels);
#endif
  
  if (nels==0)
    { /* Nothing to do */
      *count=0;
      return CALBH_ERROR_OK;
    }
  
  iCount=0;
  for (i=0;i<nels;i++)
    {
      tempbuf=calbh_read_hfifo(calb_id);
      calbb_nrtohfifo(tempbuf,bits);
      
#ifdef DEBUG
      printf("readfifo:  el %x fifodata %x \n",i,tempbuf);
#endif
      
      buffer[iCount++]=bits.fifodata;

      if (bits.nvd!=CALBB_BIT_OFF) break;

    }
   
  *count = iCount;

#ifdef DEBUG
  printf("readfifo count: %x \n",*count);
#endif
  
  return CALBH_ERROR_OK;
}


int calbh_read_pio_i_dfifo (CALBH_id calb_id,	/* IN  */
			unsigned int nels,	/* IN  */
			unsigned int *buffer,	/* OUT */
			unsigned int *count)	/* OUT, nr of els really read */
{
 int i,iCount,tempbuf;
 CALBB_DFIFO_bits bits;   

#ifdef DEBUG
 printf("readfifo size: %x \n",nels);
#endif

 if (nels==0)
   { /* Nothing to do */
     *count=0;
     return CALBH_ERROR_OK;
   }
   
 iCount=0;
 for (i=0;i<nels;i++)
     {
       tempbuf=calbh_read_dfifo(calb_id);
       calbb_nrtodfifo(tempbuf,bits);

#ifdef DEBUG
       printf("readfifo:  el %x fifodata %x \n",i,tempbuf);
#endif
       
       buffer[iCount++]=bits.fifodata;

       if (bits.nvd!=CALBB_BIT_OFF) break;

     }
   
   *count = iCount;
 
#ifdef DEBUG
 printf("readfifo count: %x \n",*count);
#endif
 
 return CALBH_ERROR_OK;
}



int calbh_read_blt_fifo(CALBH_id calb_id,	/* IN  */
			unsigned int offset,	/* IN  */
			unsigned int nrels,	/* IN  */
			unsigned int *buffer,	/* IN/OUT */
			unsigned int *count)    /* OUT, nr of els. really read */
{
 int res;
 unsigned int *DMAbuffer;
 unsigned int nrbytes;

#ifdef DEBUG
 printf("readfifo size: %x \n",nrels);
#endif

 if (nrels==0)
   { /* Nothing to do */
     *count=0;
     return CALBH_ERROR_OK;
   }

 nrbytes = nrels*4;

 /* alloc a buffer for DMA */
 /* DMAbuffer = (unsigned int *) VmeAllocateMemory(calb_id->cid,NULL,nrbytes,Vme_MEMORY_FASTEST);*/
 DMAbuffer = (unsigned int *) malloc(nrbytes);

 if (DMAbuffer==NULL)
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_readfifo_fifo","VmeAllocateBuffer error: %s",ErrorGetMessage());
     return CALBH_ERROR_UNKNOWN;
   }
 
/*  set VME channel AM to block transfer  */
 VmeSetProperty(calb_id->cid,Vme_SET_COPY_AM,0xB);
 res = VmeRead(calb_id->cid,calb_id->offs+offset,(char *) DMAbuffer,nrbytes);

 if (res==0)
   {
     ErrorSetF(CALBH_ERROR_UNKNOWN,"calbh_readfifo_fifo","VmeRead error: %s",ErrorGetMessage());
     free(DMAbuffer);
     *count=0;
     return CALBH_ERROR_UNKNOWN;
   }

 res=res>>2; /* convert in nr of elements */

#ifdef DEBUG
 printf("readfifo res size: %x \n",res);
#endif

/***
 {
   unsigned int iDMA,iCount;

   iCount=0;
   for (iDMA=0;iDMA<res;iDMA++)
     {
       CALBB_EDFIFO_bits bits;

       calbb_nr2edfifo(DMAbuffer[iDMA],bits);

      #ifdef DEBUG
       printf("readfifo %x el: %x fifodata %x \n",iDMA,DMAbuffer[iDMA],bits.fifodata);
      #endif

       buffer[iCount++]=bits.fifodata;

       if (bits.nvd!=CALBB_BIT_OFF) break;

     }

  *count = iCount;
 }
***/

#ifdef DEBUG
 printf("readfifo count: %x \n",*count);
#endif

 free(DMAbuffer);
 return CALBH_ERROR_OK;
}