From: pedja@dudley.cs.umd.edu (Pedja Bogdanovich)
Newsgroups: comp.sys.next.programmer
Subject: Storing results to FP registers from within FP signal handler
Date: 9 Apr 92 15:07:00 GMT
Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742

I am posting this for a friend.  Please reply to him at
kerner%sava@cs.umd.edu

Thanks.  Pedja

-----------------------------------------------------------------------------
I am writing a floating-point signal handler for the Motorola 68040 on
a NeXT workstation with NeXT OS 2.1 kernel.  I have obtained a loadable
kernel server from NeXT which adds user enabled floating-point exceptions.
The following file describes a problem I have in changing the result of
an overflow.

/* FILE: fpoverflow.c                                 Date: Apr 9, 1992 */
/*-----------------------------------------------------------------------------
 * This file contains retrievfpcr(), storefpcr(), storefpsr(), retrievfpsr(),
 * catcherr(), and main() for demonstrating difficulties with handling 
 * floating-point overflow signals on the NeXT computer with Motorola 68040.
 * 
 * retrievfpcr() returns the contents of the 68040's floating point control 
 * register in the d0 data register.  storefpcr() changes the contents of
 * the 68040's floating point control register to its argument's value.
 * storefpsr() changes the contents of the 68040's floating-point status
 * register to its argument's value.  retrievfpsr() returns the contents of
 * the 68040's floating-point status register in the d0 data register.
 * retrievfpcr() and storefpcr() are called in main() to store the initial 
 * floating point control register's value and to change bits 8-15 of the 
 * floating point control register to 0x08 (i.e. bits 8 through 15 are all
 * zero except bit 12 which is 1) so that overflow errors are reported when 
 * they occur.  main() also installs catcherr() as the floating-point error 
 * signal handler and stores the initial floating point error signal handler 
 * vector.  Then main() generates an overflow error and prints the result.  
 * Finally, main() restores the initial floating point control register and 
 * floating point error signal handler.
 *
 * catcherr() prints the values of the arguments which are passed to it
 * and stores MAXPOS--the largest floating-point number--in the fp0
 * floating-point register with the appropriate sign.
 *
 * Problems:
 * 1. Changes to the floating-point register within the signal handler 
 *    catcherr() have no effect on the final result upon return from the 
 *    signal handler.  How can the result of the overflow be changed?
 *---------------------------------------------------------------------------*/

/* defines from stdcsi.h: */
#define int32    int
#define uint32   unsigned int

/* the following structure is from sava-/usr/include/machine/signal.h and is 
   passed to the signal handler after the unmasked floating point signal 
   occurs */
/* Information pushed on stack when a signal is delivered.  Used by kernel to
   restore state following execution of signal handler.  Also available to 
   handler to allow it to properly restore state if non-standard  exit is
   performed.*/
struct sigcontext
   {int32 sc_onstack;  /* sigstack state to restore */
    int32 sc_mask;     /* signal mask to restore */
    int32 sc_sp;       /* sp to restore */
    int32 sc_pc;       /* pc to restore */
    int32 sc_ps;       /* psl to restore */
    int32 sc_d0;       /* d0 to restore */
   } *scp;

/* the following structure is from sava-/usr/include/sys/signal.h */
/* a sigvec structure exists for each type of signal.  sigvec structures are
   passed to the function sigvec() when changing a signal handler.  A sigvec
   structure defines the signal handler, mask, and flags for each signal.  We 
   store two such structures corresponding to the default and the new floating
   point signal handler.  The sigvec structure is similar to the action 
   structure in AIX. */
struct sigvec
   {void (*sv_handler)();
    int32 sv_mask;
    int32 sv_flags;
   } old_sigvec, new_sigvec;

/* the following externs are from both UNIX Programmer's Manual and 
   sava-/usr/include/sys/signal.h */
extern int32 sigvec(int sig,struct sigvec *newvec,struct sigvec *oldvec);

/* the following array of 2 long integers is used to hold floating-point
   stack elements */
uint32 fpr[2];

/* MAXPOS and MAXNEG are the positive and negative, largest magnitude, 64-bit
   double values.  Note that the constituent uint32's comprising these values
   on the Motorola 68040 appear in different order than those comprising these
   values on the Intel 80486. */
uint32
   maxpos[2] = {0x7fefffff,0xffffffff},
   maxneg[2] = {0xffefffff,0xffffffff};
#define MAXPOS  (*(double *)maxpos)
#define MAXNEG  (*(double *)maxneg)

/* the following macro is from both UNIX Programmer's Manual and
   sava-/usr/include/sys/signal.h */
#define SIGFPE   8

/*========================================================================*/
static int32 retrievfpcr(void)
/*------------------------------------------------------------------------
 * retrievfpcr() is an in-line assembler function which retrieves the floating
 * point control register's contents, stores them in the data register d0,
 * and returns.
 *------------------------------------------------------------------------*/
{__asm volatile ("fmovel fpcr,d0");}

/*========================================================================*/
static void storefpcr(int32 ctl)
/*------------------------------------------------------------------------
 * storefpcr() is an in-line assembler function which stores the 32-bit
 * integer argument, ctl, which is on top of the argument stack, to the 
 * data register d0, and then moves d0 to the floating point control register.
 *------------------------------------------------------------------------*/
{__asm volatile ("movel  a6@(8),d0");
 __asm volatile ("fmovel d0,fpcr");
}

/*========================================================================*/
static void storefpsr(int32 str)
/*------------------------------------------------------------------------
 * storefpsr() is an in-line assembler function which stores the 32-bit
 * integer argument, str, which is on top of the argument stack, to the 
 * data register d0, and then moves d0 to the floating point status register.
 *------------------------------------------------------------------------*/
{__asm volatile ("movel  a6@(8),d0");
 __asm volatile ("fmovel d0,fpsr");
}

/*========================================================================*/
static int32 retrievfpsr(void)
/*------------------------------------------------------------------------
 * retrievfpsr() is an in-line assembler function which retrieves the floating
 * point status register's contents, stores it in the data register d0,
 * and returns.
 *------------------------------------------------------------------------*/
{__asm volatile ("fmovel fpsr,d0");}

/*========================================================================*/
void catcherr(int32 sig,int32 code,struct sigcontext *scp)
/*------------------------------------------------------------------------
 *------------------------------------------------------------------------*/
{uint32 st,status;

 printf("\ncatcherr: sig = 0x%8x = %d, code = 0x%8x = %d",sig,sig,code,code);
 printf("\n          scp->sc_onstack = 0x%8x",scp->sc_onstack);
 printf("\n          scp->sc_mask = 0x%8x",scp->sc_mask);
 printf("\n          scp->sp = 0x%8x",scp->sc_sp);
 printf("\n          scp->pc = 0x%8x",scp->sc_pc);
 printf("\n          scp->ps = 0x%8x",scp->sc_ps);
 printf("\n          scp->d0 = 0x%8x",scp->sc_d0);

 /* save the floating point status word in status */
 status = retrievfpsr(); 

 if (status & 0x00001040) 
    {printf("\noverflow");

     /* copy the floating point register fp0 to fpr[0:1] */     
__asm volatile("fmoved _fpr,fp0");

     if (fpr[0] & 0x80000000)
        {/* the overflow is negative so copy maxneg into fp0 */
__asm volatile("fmoved _maxneg,fp0");
        }
     else
        {/* the overflow is positive so copy maxpos into fp0 */
__asm volatile("fmoved _maxpos,fp0");
        }
    }

 /* print the contents of fp0 */
__asm volatile("fmoved fp0,_fpr");
 printf("\nfp0: 0x%8lx 0x%8lx = %16.15le",fpr[0],fpr[1],*(double *)fpr);
 
 /* zero the exception status bits, and restore the floating point status 
    register */
 st = status&0xffff0000; storefpsr(st);
}

/*=========================================================================*/
main()
{double x,y;
 uint32
   rc,
   ctl0,   /* the initial contents of the floating point control register */
   ctl1;   /* ctl0 with overflow exception mask set to 1 */
 union {uint32 zint[2]; double zdbl;} z;

 /* set the floating point control register and floating point signal handler
    so that floating point overflow errors are handled by catcherr() */
 ctl0 = retrievfpcr();
 ctl1 = (ctl0&0xffff00ff)|0x00003400;
 storefpcr(ctl1);
 new_sigvec.sv_handler = catcherr;
 new_sigvec.sv_flags = 0;
 new_sigvec.sv_mask = 0;
 if ((rc = sigvec(SIGFPE,&new_sigvec,&old_sigvec))!=0)
    printf("\nmain: sigvec() failed installing new sigvec");

 /* generate a floating point overflow error */
 x = 1.E200; y = 1.E300;
 z.zdbl = x*y;
 printf("\n1.E200*1.E300 = %16.15le  0x%8x 0x%8x",z.zdbl,z.zint[0],z.zint[1]);

 /* restore the floating point control register and floating point signal
    handler to their defaults */
 storefpcr(ctl0);
 if ((rc = sigvec(SIGFPE,&old_sigvec,&new_sigvec))!=0)
    printf("\nmain: sigvec failed restoring default sigvec");
 printf("\n");
}
/* end of fpoverflow.c */