113576Sdlw /* #define OLD_BSD if you're running < 4.2bsd */ 213576Sdlw /* 3*14635Sdlw char id_trpfpe[] = "@(#)trpfpe_.c 1.3"; 413576Sdlw * 513576Sdlw * Fortran floating-point error handler 613576Sdlw * 713576Sdlw * Synopsis: 813576Sdlw * call trpfpe (n, retval) 913576Sdlw * causes floating point faults to be trapped, with the 1013576Sdlw * first 'n' errors getting a message printed. 1113576Sdlw * 'retval' is put in place of the bad result. 1213576Sdlw * k = fpecnt() 1313576Sdlw * causes 'k' to get the number of errors since the 1413576Sdlw * last call to trpfpe(). 1513576Sdlw * 1613576Sdlw * common /fpeflt/ fpflag 1713576Sdlw * logical fpflag 1813576Sdlw * fpflag will become .true. on faults 1913576Sdlw * 2013576Sdlw * David Wasley, UCBerkeley, June 1983. 2113576Sdlw */ 2213576Sdlw 2313576Sdlw 2413576Sdlw #include <stdio.h> 2513576Sdlw #include <signal.h> 2613576Sdlw #include "opcodes.h" 2713576Sdlw #include "operand.h" 2813576Sdlw #include "../libI77/fiodefs.h" 2913576Sdlw 3013576Sdlw #define SIG_VAL int (*)() 3113576Sdlw 3213576Sdlw #if vax /* only works on VAXen */ 3313576Sdlw 3413576Sdlw struct arglist { /* what AP points to */ 3513576Sdlw long al_numarg; /* only true in CALLS format */ 3613576Sdlw long al_arg[256]; 3713576Sdlw }; 3813576Sdlw 3913576Sdlw struct cframe { /* VAX call frame */ 4013576Sdlw long cf_handler; 4113576Sdlw unsigned short cf_psw; 4213576Sdlw unsigned short cf_mask; 4313576Sdlw struct arglist *cf_ap; 4413576Sdlw struct cframe *cf_fp; 4513576Sdlw char *cf_pc; 4613576Sdlw }; 4713576Sdlw 4813576Sdlw /* 4913576Sdlw * bits in the PSW 5013576Sdlw */ 5113576Sdlw #define PSW_V 0x2 5213576Sdlw #define PSW_FU 0x40 5313576Sdlw #define PSW_IV 0x20 5413576Sdlw 5513576Sdlw /* 5613576Sdlw * where the registers are stored as we see them in the handler 5713576Sdlw */ 5813576Sdlw struct reg0_6 { 5913576Sdlw long reg[7]; 6013576Sdlw }; 6113576Sdlw 6213576Sdlw struct reg7_11 { 6313576Sdlw long reg[5]; 6413576Sdlw }; 6513576Sdlw 6613576Sdlw #define iR0 reg0_6->reg[0] 6713576Sdlw #define iR1 reg0_6->reg[1] 6813576Sdlw #define iR2 reg0_6->reg[2] 6913576Sdlw #define iR3 reg0_6->reg[3] 7013576Sdlw #define iR4 reg0_6->reg[4] 7113576Sdlw #define iR5 reg0_6->reg[5] 7213576Sdlw #define iR6 reg0_6->reg[6] 7313576Sdlw #define iR7 reg7_11->reg[0] 7413576Sdlw #define iR8 reg7_11->reg[1] 7513576Sdlw #define iR9 reg7_11->reg[2] 7613576Sdlw #define iR10 reg7_11->reg[3] 7713576Sdlw #define iR11 reg7_11->reg[4] 7813576Sdlw 7913576Sdlw union objects { /* for load/store */ 8013576Sdlw char ua_byte; 8113576Sdlw short ua_word; 8213576Sdlw long ua_long; 8313576Sdlw float ua_float; 8413576Sdlw double ua_double; 8513576Sdlw union objects *ua_anything; 8613576Sdlw }; 8713576Sdlw 8813576Sdlw typedef union objects anything; 8913576Sdlw enum object_type { BYTE, WORD, LONG, FLOAT, QUAD, DOUBLE, UNKNOWN }; 9013576Sdlw 9113576Sdlw 9213576Sdlw /* 9313576Sdlw * assembly language assist 9413576Sdlw * There are some things you just can't do in C 9513576Sdlw */ 9613576Sdlw asm(".text"); 9713576Sdlw 9813576Sdlw struct cframe *myfp(); 9913576Sdlw asm("_myfp: .word 0x0"); 10013576Sdlw asm("movl 12(fp),r0"); 10113576Sdlw asm("ret"); 10213576Sdlw 10313576Sdlw struct arglist *myap(); 10413576Sdlw asm("_myap: .word 0x0"); 10513576Sdlw asm("movl 8(fp),r0"); 10613576Sdlw asm("ret"); 10713576Sdlw 10813576Sdlw char *mysp(); 10913576Sdlw asm("_mysp: .word 0x0"); 11013576Sdlw asm("extzv $30,$2,4(fp),r0"); 11113576Sdlw asm("addl2 ap,r0"); /* SP in caller is AP+4 here + SPA bits! */ 11213576Sdlw asm("addl2 $4,r0"); 11313576Sdlw asm("ret"); 11413576Sdlw 11513576Sdlw char *mypc(); 11613576Sdlw asm("_mypc: .word 0x0"); 11713576Sdlw asm("movl 16(fp),r0"); 11813576Sdlw asm("ret"); 11913576Sdlw 12013576Sdlw asm(".data"); 12113576Sdlw 12213576Sdlw 12313576Sdlw /* 12413576Sdlw * Where interrupted objects are 12513576Sdlw */ 12613576Sdlw static struct cframe **ifp; /* addr of saved FP */ 12713576Sdlw static struct arglist **iap; /* addr of saved AP */ 12813576Sdlw static char *isp; /* value of interrupted SP */ 12913576Sdlw static char **ipc; /* addr of saved PC */ 13013576Sdlw static struct reg0_6 *reg0_6;/* registers 0-6 are saved on the exception */ 13113576Sdlw static struct reg7_11 *reg7_11;/* we save 7-11 by our entry mask */ 13213576Sdlw static anything *result_addr; /* where the dummy result goes */ 13313576Sdlw static enum object_type result_type; /* what kind of object it is */ 13413576Sdlw 13513576Sdlw /* 13613576Sdlw * some globals 13713576Sdlw */ 13813576Sdlw static union { 13913576Sdlw long rv_long[2]; 14013576Sdlw float rv_float; 14113576Sdlw double rv_double; 14213576Sdlw } retval; /* the user specified dummy result */ 14313576Sdlw static int max_messages = 1; /* the user can tell us */ 14413576Sdlw static int fpe_count = 0; /* how bad is it ? */ 14513576Sdlw long fpeflt_ = 0; /* fortran "common /fpeflt/ flag" */ 14613576Sdlw static int (*sigfpe_dfl)() = SIG_DFL; /* if we can't fix it ... */ 14713576Sdlw 14813576Sdlw /* 14913576Sdlw * The fortran unit control table 15013576Sdlw */ 15113576Sdlw extern unit units[]; 15213576Sdlw 15313576Sdlw /* 15413576Sdlw * Fortran message table is in main 15513576Sdlw */ 15613576Sdlw struct msgtbl { 15713576Sdlw char *mesg; 15813576Sdlw int dummy; 15913576Sdlw }; 16013576Sdlw extern struct msgtbl act_fpe[]; 16113576Sdlw 16213576Sdlw 16313576Sdlw /* 16413576Sdlw * Get the address of the (saved) next operand & update saved PC. 16513576Sdlw * The major purpose of this is to determine where to store the result. 16613576Sdlw * There is one case we can't deal with: -(SP) or (SP)+ 16713576Sdlw * since we can't change the size of the stack. 16813576Sdlw * Let's just hope compilers don't generate that for results. 16913576Sdlw */ 17013576Sdlw 17113576Sdlw anything * 17213576Sdlw get_operand (oper_size) 17313576Sdlw int oper_size; /* size of operand we expect */ 17413576Sdlw { 17513576Sdlw register int regnum; 17613576Sdlw register int operand_code; 17713576Sdlw int index; 17813576Sdlw anything *oper_addr; 17913576Sdlw anything *reg_addr; 18013576Sdlw 18113576Sdlw regnum = (**ipc & 0xf); 18213576Sdlw if (regnum == PC) 18313576Sdlw operand_code = (*(*ipc)++ & 0xff); 18413576Sdlw else 18513576Sdlw operand_code = (*(*ipc)++ & 0xf0); 18613576Sdlw if (regnum <= R6) 18713576Sdlw reg_addr = (anything *)®0_6->reg[regnum]; 18813576Sdlw else if (regnum <= R11) 18913576Sdlw reg_addr = (anything *)®7_11->reg[regnum]; 19013576Sdlw else if (regnum == AP) 19113576Sdlw reg_addr = (anything *)iap; 19213576Sdlw else if (regnum == FP) 19313576Sdlw reg_addr = (anything *)ifp; 19413576Sdlw else if (regnum == SP) 19513576Sdlw reg_addr = (anything *)&isp; /* We saved this ourselves */ 19613576Sdlw else if (regnum == PC) 19713576Sdlw reg_addr = (anything *)ipc; 19813576Sdlw 19913576Sdlw 20013576Sdlw switch (operand_code) 20113576Sdlw { 20213576Sdlw case IMMEDIATE: 20313576Sdlw oper_addr = (anything *)(*ipc); 20413576Sdlw *ipc += oper_size; 20513576Sdlw return(oper_addr); 20613576Sdlw 20713576Sdlw case ABSOLUTE: 20813576Sdlw oper_addr = (anything *)(**ipc); 20913576Sdlw *ipc += sizeof (anything *); 21013576Sdlw return(oper_addr); 21113576Sdlw 21213576Sdlw case LITERAL0: 21313576Sdlw case LITERAL1: 21413576Sdlw case LITERAL2: 21513576Sdlw case LITERAL3: 21613576Sdlw /* we don't care about the address of these */ 21713576Sdlw return((anything *)0); 21813576Sdlw 21913576Sdlw case INDEXED: 22013576Sdlw index = reg_addr->ua_long * oper_size; 22113576Sdlw oper_addr = (anything *)(get_operand(sizeof (long))->ua_long + index); 22213576Sdlw return(oper_addr); 22313576Sdlw 22413576Sdlw case REGISTER: 22513576Sdlw return(reg_addr); 22613576Sdlw 22713576Sdlw case REGDEFERED: 22813576Sdlw return(reg_addr->ua_anything); 22913576Sdlw 23013576Sdlw case AUTODEC: 23113576Sdlw if (regnum == SP) 23213576Sdlw { 23313576Sdlw fprintf(stderr, "trp: can't fix -(SP) operand\n"); 23413576Sdlw exit(1); 23513576Sdlw } 23613576Sdlw reg_addr->ua_long -= oper_size; 23713576Sdlw oper_addr = reg_addr->ua_anything; 23813576Sdlw return(oper_addr); 23913576Sdlw 24013576Sdlw case AUTOINC: 24113576Sdlw if (regnum == SP) 24213576Sdlw { 24313576Sdlw fprintf(stderr, "trp: can't fix (SP)+ operand\n"); 24413576Sdlw exit(1); 24513576Sdlw } 24613576Sdlw oper_addr = reg_addr->ua_anything; 24713576Sdlw reg_addr->ua_long += oper_size; 24813576Sdlw return(oper_addr); 24913576Sdlw 25013576Sdlw case AUTOINCDEF: 25113576Sdlw if (regnum == SP) 25213576Sdlw { 25313576Sdlw fprintf(stderr, "trp: can't fix @(SP)+ operand\n"); 25413576Sdlw exit(1); 25513576Sdlw } 25613576Sdlw oper_addr = (reg_addr->ua_anything)->ua_anything; 25713576Sdlw reg_addr->ua_long += sizeof (anything *); 25813576Sdlw return(oper_addr); 25913576Sdlw 26013576Sdlw case BYTEDISP: 26113576Sdlw case BYTEREL: 26213626Sdlw index = ((anything *)(*ipc))->ua_byte; 26313626Sdlw *ipc += sizeof (char); /* do it now in case reg==PC */ 26413626Sdlw oper_addr = (anything *)(index + reg_addr->ua_long); 26513576Sdlw return(oper_addr); 26613576Sdlw 26713576Sdlw case BYTEDISPDEF: 26813576Sdlw case BYTERELDEF: 26913626Sdlw index = ((anything *)(*ipc))->ua_byte; 27013626Sdlw *ipc += sizeof (char); /* do it now in case reg==PC */ 27113626Sdlw oper_addr = (anything *)(index + reg_addr->ua_long); 27213576Sdlw oper_addr = oper_addr->ua_anything; 27313576Sdlw return(oper_addr); 27413576Sdlw 27513576Sdlw case WORDDISP: 27613576Sdlw case WORDREL: 27713626Sdlw index = ((anything *)(*ipc))->ua_word; 27813626Sdlw *ipc += sizeof (short); /* do it now in case reg==PC */ 27913626Sdlw oper_addr = (anything *)(index + reg_addr->ua_long); 28013576Sdlw return(oper_addr); 28113576Sdlw 28213576Sdlw case WORDDISPDEF: 28313576Sdlw case WORDRELDEF: 28413626Sdlw index = ((anything *)(*ipc))->ua_word; 28513626Sdlw *ipc += sizeof (short); /* do it now in case reg==PC */ 28613626Sdlw oper_addr = (anything *)(index + reg_addr->ua_long); 28713576Sdlw oper_addr = oper_addr->ua_anything; 28813576Sdlw return(oper_addr); 28913576Sdlw 29013576Sdlw case LONGDISP: 29113576Sdlw case LONGREL: 29213626Sdlw index = ((anything *)(*ipc))->ua_long; 29313626Sdlw *ipc += sizeof (long); /* do it now in case reg==PC */ 29413626Sdlw oper_addr = (anything *)(index + reg_addr->ua_long); 29513576Sdlw return(oper_addr); 29613576Sdlw 29713576Sdlw case LONGDISPDEF: 29813576Sdlw case LONGRELDEF: 29913626Sdlw index = ((anything *)(*ipc))->ua_long; 30013626Sdlw *ipc += sizeof (long); /* do it now in case reg==PC */ 30113626Sdlw oper_addr = (anything *)(index + reg_addr->ua_long); 30213576Sdlw oper_addr = oper_addr->ua_anything; 30313576Sdlw return(oper_addr); 30413576Sdlw 30513576Sdlw /* NOTREACHED */ 30613576Sdlw } 30713576Sdlw } 30813576Sdlw 30913576Sdlw /* 31013576Sdlw * Trap & repair floating exceptions so that a program may proceed. 31113576Sdlw * There is no notion of "correctness" here; just the ability to continue. 31213576Sdlw * 31313576Sdlw * The on_fpe() routine first checks the type code to see if the 31413576Sdlw * exception is repairable. If so, it checks the opcode to see if 31513576Sdlw * it is one that it knows. If this is true, it then simulates the 31613576Sdlw * VAX cpu in retrieving operands in order to increment iPC correctly. 31713576Sdlw * It notes where the result of the operation would have been stored 31813576Sdlw * and substitutes a previously supplied value. 31913576Sdlw */ 32013576Sdlw 32113576Sdlw #ifdef OLD_BSD 32213576Sdlw on_fpe(signo, code, myaddr, pc, ps) 32313576Sdlw int signo, code, ps; 32413576Sdlw char *myaddr, *pc; 32513576Sdlw #else 32613576Sdlw on_fpe(signo, code, sc, grbg) 32713576Sdlw int signo, code; 32813576Sdlw struct sigcontext *sc; 32913576Sdlw #endif 33013576Sdlw { 33113576Sdlw /* 33213576Sdlw * There must be at least 5 register variables here 33313576Sdlw * so our entry mask will save R11-R7. 33413576Sdlw */ 33513576Sdlw register long *stk; 33613576Sdlw register long *sp; 33713576Sdlw register struct arglist *ap; 33813576Sdlw register struct cframe *fp; 33913576Sdlw register FILE *ef; 34013576Sdlw 34113576Sdlw ef = units[STDERR].ufd; /* fortran error stream */ 34213576Sdlw 34313576Sdlw switch (code) 34413576Sdlw { 34513576Sdlw case FPE_INTOVF_TRAP: /* integer overflow */ 34613576Sdlw case FPE_INTDIV_TRAP: /* integer divide by zero */ 34713576Sdlw case FPE_FLTOVF_TRAP: /* floating overflow */ 34813576Sdlw case FPE_FLTDIV_TRAP: /* floating/decimal divide by zero */ 34913576Sdlw case FPE_FLTUND_TRAP: /* floating underflow */ 35013576Sdlw case FPE_DECOVF_TRAP: /* decimal overflow */ 35113576Sdlw case FPE_SUBRNG_TRAP: /* subscript out of range */ 35213576Sdlw default: 35313576Sdlw cant_fix: 35413576Sdlw if (sigfpe_dfl > (SIG_VAL)7) /* user specified */ 35513576Sdlw #ifdef OLD_BSD 35613576Sdlw return((*sigfpe_dfl)(signo, code, myaddr, pc, ps)); 35713576Sdlw #else 35813576Sdlw return((*sigfpe_dfl)(signo, code, sc, grbg)); 35913576Sdlw #endif 36013576Sdlw else 36113576Sdlw #ifdef OLD_BSD 36213576Sdlw sigdie(signo, code, myaddr, pc, ps); 36313576Sdlw #else 36413576Sdlw sigdie(signo, code, sc, grbg); 36513576Sdlw #endif 36613576Sdlw /* NOTREACHED */ 36713576Sdlw 36813576Sdlw case FPE_FLTOVF_FAULT: /* floating overflow fault */ 36913576Sdlw case FPE_FLTDIV_FAULT: /* divide by zero floating fault */ 37013576Sdlw case FPE_FLTUND_FAULT: /* floating underflow fault */ 37113576Sdlw if (++fpe_count <= max_messages) { 37213576Sdlw fprintf(ef, "trpfpe: %s", 37313576Sdlw act_fpe[code-1].mesg); 37413576Sdlw if (fpe_count == max_messages) 37513576Sdlw fprintf(ef, ": No more messages will be printed.\n"); 37613576Sdlw else 37713576Sdlw fputc('\n', ef); 37813576Sdlw } 37913576Sdlw fpeflt_ = -1; 38013576Sdlw break; 38113576Sdlw } 38213576Sdlw 38313576Sdlw ap = myap(); /* my arglist pointer */ 38413576Sdlw fp = myfp(); /* my frame pointer */ 38513576Sdlw ifp = &(fp->cf_fp)->cf_fp; /* user's stored in next frame back */ 38613576Sdlw iap = &(fp->cf_fp)->cf_ap; 38713576Sdlw /* 38813576Sdlw * these are likely to be system dependent 38913576Sdlw */ 39013576Sdlw reg0_6 = (struct reg0_6 *)((char *)fp->cf_fp + sizeof (struct cframe)); 39113576Sdlw reg7_11 = (struct reg7_11 *)((char *)fp->cf_fp - sizeof (struct reg7_11)); 39213576Sdlw 39313576Sdlw #ifdef OLD_BSD 39413576Sdlw ipc = &pc; 39513576Sdlw isp = (char *)&ap->al_arg[ap->al_numarg + 2]; /* assumes 2 dummys */ 39613576Sdlw ps &= ~(PSW_V|PSW_FU); 39713576Sdlw #else 39813576Sdlw ipc = (char **)&sc->sc_pc; 399*14635Sdlw isp = (char *)sc + sizeof (struct sigcontext); 40013576Sdlw sc->sc_ps &= ~(PSW_V|PSW_FU); 40113576Sdlw #endif 40213576Sdlw 40313576Sdlw 40413576Sdlw switch (*(*ipc)++) 40513576Sdlw { 40613576Sdlw case ADDD3: 40713576Sdlw case DIVD3: 40813576Sdlw case MULD3: 40913576Sdlw case SUBD3: 41013576Sdlw (void) get_operand(sizeof (double)); 41113576Sdlw /* intentional fall-thru */ 41213576Sdlw 41313576Sdlw case ADDD2: 41413576Sdlw case DIVD2: 41513576Sdlw case MULD2: 41613576Sdlw case SUBD2: 41713576Sdlw case MNEGD: 41813576Sdlw case MOVD: 41913576Sdlw (void) get_operand(sizeof (double)); 42013576Sdlw result_addr = get_operand(sizeof (double)); 42113576Sdlw result_type = DOUBLE; 42213576Sdlw break; 42313576Sdlw 42413576Sdlw case ADDF3: 42513576Sdlw case DIVF3: 42613576Sdlw case MULF3: 42713576Sdlw case SUBF3: 42813576Sdlw (void) get_operand(sizeof (float)); 42913576Sdlw /* intentional fall-thru */ 43013576Sdlw 43113576Sdlw case ADDF2: 43213576Sdlw case DIVF2: 43313576Sdlw case MULF2: 43413576Sdlw case SUBF2: 43513576Sdlw case MNEGF: 43613576Sdlw case MOVF: 43713576Sdlw (void) get_operand(sizeof (float)); 43813576Sdlw result_addr = get_operand(sizeof (float)); 43913576Sdlw result_type = FLOAT; 44013576Sdlw break; 44113576Sdlw 44213576Sdlw case CVTDF: 44313576Sdlw (void) get_operand(sizeof (double)); 44413576Sdlw result_addr = get_operand(sizeof (float)); 44513576Sdlw result_type = FLOAT; 44613576Sdlw break; 44713576Sdlw 44813576Sdlw case CVTFD: 44913576Sdlw (void) get_operand(sizeof (float)); 45013576Sdlw result_addr = get_operand(sizeof (double)); 45113576Sdlw result_type = DOUBLE; 45213576Sdlw break; 45313576Sdlw 45413576Sdlw case EMODF: 45513576Sdlw case EMODD: 45613576Sdlw fprintf(ef, "trpfpe: can't fix emod yet\n"); 45713576Sdlw goto cant_fix; 45813576Sdlw 45913576Sdlw case POLYF: 46013576Sdlw case POLYD: 46113576Sdlw fprintf(ef, "trpfpe: can't fix poly yet\n"); 46213576Sdlw goto cant_fix; 46313576Sdlw 46413576Sdlw case ACBD: 46513576Sdlw case ACBF: 46613576Sdlw case CMPD: 46713576Sdlw case CMPF: 46813576Sdlw case TSTD: 46913576Sdlw case TSTF: 47013576Sdlw case CVTDB: 47113576Sdlw case CVTDL: 47213576Sdlw case CVTDW: 47313576Sdlw case CVTFB: 47413576Sdlw case CVTFL: 47513576Sdlw case CVTFW: 47613576Sdlw case CVTRDL: 47713576Sdlw case CVTRFL: 47813576Sdlw /* These can generate only reserved operand faults */ 47913576Sdlw /* They are shown here for completeness */ 48013576Sdlw 48113576Sdlw default: 48213576Sdlw fprintf(stderr, "trp: opcode 0x%02x unknown\n", 48313576Sdlw *(--(*ipc)) & 0xff); 48413576Sdlw goto cant_fix; 48513576Sdlw /* NOTREACHED */ 48613576Sdlw } 48713576Sdlw 48813576Sdlw if (result_type == FLOAT) 48913576Sdlw result_addr->ua_float = retval.rv_float; 49013576Sdlw else 49113576Sdlw { 49213576Sdlw if (result_addr == (anything *)&iR6) 49313576Sdlw { /* 49413576Sdlw * special case - the R6/R7 pair is stored apart 49513576Sdlw */ 49613576Sdlw result_addr->ua_long = retval.rv_long[0]; 49713576Sdlw ((anything *)&iR7)->ua_long = retval.rv_long[1]; 49813576Sdlw } 49913576Sdlw else 50013576Sdlw result_addr->ua_double = retval.rv_double; 50113576Sdlw } 50213576Sdlw signal(SIGFPE, on_fpe); 50313576Sdlw } 50413576Sdlw #endif vax 50513576Sdlw 50613576Sdlw trpfpe_ (count, rval) 50713576Sdlw long *count; /* how many to announce */ 50813576Sdlw double *rval; /* dummy return value */ 50913576Sdlw { 51013576Sdlw #if vax 51113576Sdlw max_messages = *count; 51213576Sdlw retval.rv_double = *rval; 51313576Sdlw sigfpe_dfl = signal(SIGFPE, on_fpe); 51413576Sdlw fpe_count = 0; 51513576Sdlw #endif 51613576Sdlw } 51713576Sdlw 51813576Sdlw long 51913576Sdlw fpecnt_ () 52013576Sdlw { 52113576Sdlw #if vax 52213576Sdlw return (fpe_count); 52313576Sdlw #else 52413576Sdlw return (0L); 52513576Sdlw #endif 52613576Sdlw } 52713576Sdlw 528