|
|
1.1 root 1: /* FLOATING POINT OVERFLOW FAULT HANDLER.
2: * UNIVERSITY OF NEW MEXICO.
3: * This set of routines handles floating faults for most instructions
4: * that can generate them. Two notable exceptions are EMOD and POLY.
5: * These two instructions are not generated by the C compiler however.
6: * The routines are written generically enough that it would be easy
7: * to modify the code to handle all possible arithmetic exceptions on
8: * VAX 11/730s, 11/750s and 11/780's (after ECO #7). Before DEC's
9: * Engineering Change Order #7 * VAX 11/780s trapped on these errors so
10: * they must be handled in a TOTALLY different manner.
11: *
12: * To use these routines from Fortran:
13: *
14: * call ofault(nummsg) will initialize the fault handler and set
15: * the count for number of messages to be
16: * printed to nummsg .
17: *
18: * k = kfault(dummy) returns a count of the number of overflows
19: * and divisions by zero seen so far.
20: *
21: * The result of all floating point expononent overflows and
22: * floating point divisions by zero will be the largest possible
23: * floating point number, which is approximately 1.7e+38. Floating
24: * underflows (if you have the fault enabled) will return the smallest
25: * floating point number.
26: *
27: * The strategy goes like this: When VAX detects a fault some
28: * registers, the PC of the offending instruction and the PSL are saved on the
29: * user stack. When we get this we go through all the operand specifiers
30: * performing address calculation in a manner compatible with the machine.
31: * What this means is that auto-increment and its kin are performed on the
32: * register contents pushed on the stack. When we find the destination
33: * address of the offending instruction, the maximum value for the data
34: * type is placed there for an error return. Our working PC now points to
35: * the next instruction past the bad one. We load the saved PC on the stack
36: * with our working PC and return. Thus the instruction really never gets
37: * executed BUT we maintain consistency with pointers, etc in the code.
38: *
39: * One might notice that little discrepancies in the addressing modes, like
40: * the fact that you aren't supposed to use general mode with the PC, are
41: * not checked for. This is so that the code runs a bit quicker. For most
42: * of these VAX will give a different fault long before attempting the
43: * arithmetic and the others the user is warned against.
44: *
45: * Lee Ward, University of New Mexico, 7/2/83.
46: * Inspired by Gary Klimowicz's pre ECO #7 trap handling routines.
47: */
48:
49: #include <signal.h>
50: #include <stdio.h>
51: #include "ofault.h"
52:
53: #ifndef FPE_FLTOVF_FAULT
54: #define FPE_FLTOVF_FAULT K_FLTOVF
55: #define FPE_FLTDIV_FAULT K_FLTDIV
56: #define FPE_FLTUND_FAULT K_FLTUND
57: #endif
58:
59: #define size_of(x) (x)
60:
61: /* GLOBALS */
62: char *pc; /* PC to be used by the routines */
63: long *regs0t6; /* Saved at interrupt time */
64: long *regs7t11; /* We cause these to be pushed */
65: int num_msgs; /* Maximum number of error messages */
66: int kfault; /* Overflows detected */
67:
68: /* ofault_ Set up to trap arithmetic exceptions reported by SIGFPE.
69: * Calling this function a second (or third or ...) time
70: * will cause the number of error messages to be printed
71: * to be reset.
72: */
73: ofault_(nummsgs)
74: int *nummsgs;
75: {
76: extern fp_except();
77: extern int num_msgs;
78: extern int kfault;
79:
80: num_msgs = *nummsgs;
81: kfault = 0;
82: signal (SIGFPE, fp_except);
83: }
84:
85: /* kfault_ Return the number of overflows encountered
86: */
87: int kfault_ ()
88: {
89: extern int kfault;
90:
91: return (kfault);
92: }
93:
94: /* fp_except Handle floating point faults
95: *
96: * A little expalnation here as this is somewhat confusing. At interrupt
97: * time the hardware pushes registers 0 through 6 on the users stack. These
98: * can be found at &signo - 8. The others we arrange to have pushed into
99: * the local call frame with the below register definitions. It is important
100: * that these are not changed. Note that fault_pc and psl are effectively
101: * passed by reference as the REI instruction causes them to be reloaded.
102: * It is possible to play with these a little. You just must be careful
103: * not to put the processor into an "undefined state." For reference see
104: * the Architecture handbook (REI instruction).
105: */
106: fp_except (signo, f_type, myaddr, fault_pc, psl)
107: int f_type;
108: unsigned psl;
109: char *fault_pc;
110: {
111: long first_local[1]; /* MUST be first! */
112: register int a, b, c, d, e; /* Don't change! */
113: extern int num_msgs;
114: extern int kfault;
115:
116: /* Get info off of stack */
117: regs7t11 = &first_local[0];
118: regs0t6 = &signo - 8;
119: pc = fault_pc;
120:
121: /* Two switches here. One to print an appropriate message and one
122: * to handle the fault.
123: */
124: if (kfault < num_msgs)
125: switch (f_type) {
126: case FPE_FLTOVF_FAULT:
127: fprintf (stderr, "Floating overflow!\n");
128: break;
129: case FPE_FLTDIV_FAULT:
130: fprintf (stderr, "Floating division by zero!\n");
131: break;
132: case FPE_FLTUND_FAULT:
133: fprintf (stderr, "Floating underflow!\n");
134: break;
135: default: /* Let the handler switch deal with this */
136: break;
137: }
138:
139: switch (f_type) {
140: case FPE_FLTOVF_FAULT:
141: case FPE_FLTDIV_FAULT:
142: flt_ov_fault();
143: break;
144: case FPE_FLTUND_FAULT: /* You have to enable first */
145: flt_und_fault();
146: break;
147: default:
148: err ("Cannot handle fault 0X%x", f_type);
149: }
150: ++kfault;
151:
152: fault_pc = pc;
153: signal (SIGFPE, fp_except);
154:
155: if (kfault == num_msgs)
156: err ("No more overflow messages will be printed");
157:
158: return; /* Ala REI */
159: }
160:
161: /* flt_ov_fault Handle a floating overflow fault. A misnomer as we handle
162: * division by zero in the same manner.
163: */
164: flt_ov_fault ()
165: {
166: int opcode;
167: int x;
168: int dest;
169: int type;
170: char *addr;
171: extern char *pc;
172: extern ANYTYPE *fetch();
173: extern char *addr_reg();
174: extern char *opnd_addr();
175:
176: opcode = (int )fetch()->byte;
177: pc += 1;
178:
179: dest = dest_opnd(opcode);
180: for (x = 1; x <= num_opnd(opcode); ++x) {
181: if ((type = get_type(opcode, x)) == GARBAGE)
182: err ("data type of operand? (op = 0X%x, operand %d)"
183: , opcode, x);
184: addr = opnd_addr (type);
185: if (x == dest)
186: load (addr, type, MAXFLOAT, MAXDOUBLR);
187: }
188: }
189:
190: /* flt_und_fault Handle a floating underflow fault.
191: */
192: flt_und_fault ()
193: {
194: int opcode;
195: int x;
196: int dest;
197: int type;
198: char *addr;
199: extern char *pc;
200: extern ANYTYPE *fetch();
201: extern char *addr_reg();
202: extern char *opnd_addr();
203:
204: opcode = (int )fetch()->byte;
205: pc += 1;
206:
207: dest = dest_opnd(opcode);
208: for (x = 1; x <= num_opnd(opcode); ++x) {
209: if ((type = get_type(opcode, x)) == GARBAGE)
210: err ("data type of operand? (op = 0X%x, operand %d)"
211: , opcode, x);
212: addr = opnd_addr (type);
213: if (x == dest)
214: load (addr, type, MINFLOAT, MINDOUBLR);
215: }
216: }
217:
218: /* dest_opnd Given an opcode return the number of the operand specifier
219: * that is the destination.
220: */
221: dest_opnd (opcode)
222: int opcode;
223: {
224:
225: switch (opcode) {
226:
227: case CVTDF:
228: case ADDD2:
229: case SUBD2:
230: case MULD2:
231: case DIVD2:
232: case ADDF2:
233: case SUBF2:
234: case MULF2:
235: case DIVF2: return (2);
236: case ADDF3:
237: case SUBF3:
238: case MULF3:
239: case DIVF3:
240: case ADDD3:
241: case SUBD3:
242: case MULD3:
243: case DIVD3: return (3);
244: default:
245: err ("cannot determine destination operand (opcode = %x)");
246: return (-1);
247: }
248: }
249:
250: /* num_opnd Given an opcode return the number of operand specifiers
251: * associated with it.
252: */
253: num_opnd (opcode)
254: int opcode;
255: {
256:
257: switch (opcode) {
258:
259: case CVTDF:
260: case ADDD2:
261: case SUBD2:
262: case MULD2:
263: case DIVD2:
264: case ADDF2:
265: case SUBF2:
266: case MULF2:
267: case DIVF2: return (2);
268: case ADDF3:
269: case SUBF3:
270: case MULF3:
271: case DIVF3:
272: case ADDD3:
273: case SUBD3:
274: case MULD3:
275: case DIVD3: return (3);
276: default:
277: err ("cannot determine number of operands (opcode = %x)"
278: , opcode);
279: return (-1);
280: }
281: }
282:
283: /* opnd_addr Given an operand specifier address, return the address
284: * of the operand.
285: * Note: PC is incremented dynamically here as that is the way
286: * the VAX goes through its operand specifiers. Also any
287: * increment/decrement modes cause changes according to the
288: * documentation.
289: */
290: char *opnd_addr(type)
291: int type;
292: {
293: register unsigned temp;
294: register char *tptr;
295: register int mode;
296: register int reg;
297: extern ANYTYPE *fetch();
298: extern char *addr_reg();
299: extern char *pc;
300:
301: temp = (unsigned )fetch()->byte;
302: mode = (temp & MODE_MASK) >> 4;
303: reg = temp & REG_MASK;
304: pc += 1;
305: switch (mode) {
306: case INDEXED:
307: tptr = (char *)(reg_cont (reg) * size_of (type));
308: return (tptr + (int )opnd_addr(LONG));
309: case GENERAL:
310: return (addr_reg(reg));
311: case REG_DEFRD:
312: return ((char *)reg_cont (reg));
313: case AUTO_DEC:
314: tptr = addr_reg(reg);
315: *tptr -= size_of (type);
316: return ((char *)reg_cont(reg));
317: case AUTO_INC:
318: tptr = addr_reg(reg);
319: *tptr += size_of (type);
320: return ((char *)(reg_cont(reg) - size_of(type)));
321: case AUTO_INC_DEF:
322: tptr = addr_reg (reg);
323: *tptr += size_of (LONG);
324: return ((char *)*(char *)(reg_cont(reg) - size_of(LONG)));
325: case BYTE_DISP:
326: tptr = (char *)fetch()->byte;
327: pc += 1;
328: return (tptr + reg_cont(reg));
329: case BYTE_DISP_DEF:
330: tptr = (char *)fetch()->byte;
331: pc += 1;
332: return ((char *)*(long *)(tptr + reg_cont(reg)));
333: case WORD_DISP:
334: tptr = (char *)fetch()->word;
335: pc += 2;
336: return (tptr + reg_cont(reg));
337: case WORD_DISP_DEF:
338: tptr = (char *)fetch()->word;
339: pc += 2;
340: return ((char *)*(long *)(tptr + reg_cont(reg)));
341: case LONG_DISP:
342: tptr = (char *)fetch()->llong;
343: pc += 4;
344: return (tptr + reg_cont(reg));
345: case LONG_DISP_DEF:
346: tptr = (char *)fetch()->llong;
347: pc += 4;
348: return ((char *)*(long *)(tptr + reg_cont(reg)));
349: default: /* Cannot screw with literals */
350: err ("cannot get address of operand for type 0X%x", type);
351: return (NULL);
352: }
353: }
354:
355: /* get_type: Given an opcode and the number of the operand return the data
356: * type of the operand.
357: */
358: get_type (opcode, opernd_num)
359: int opcode;
360: int opernd_num;
361: {
362:
363: switch (opcode) {
364:
365: case CVTDF: switch (opernd_num) {
366: case 1: return (DOUBLE);
367: case 2: return (FLOAT);
368: default: return (GARBAGE);
369: }
370: case ADDF2:
371: case SUBF2:
372: case MULF2:
373: case DIVF2: switch (opernd_num) {
374: case 1:
375: case 2: return (FLOAT);
376: default: return (GARBAGE);
377: }
378: case ADDF3:
379: case SUBF3:
380: case MULF3:
381: case DIVF3: switch (opernd_num) {
382: case 1:
383: case 2:
384: case 3: return (FLOAT);
385: default: return (GARBAGE);
386: }
387: case ADDD2:
388: case SUBD2:
389: case MULD2:
390: case DIVD2: switch (opernd_num) {
391: case 1:
392: case 2: return (DOUBLE);
393: default: return (GARBAGE);
394: }
395: case ADDD3:
396: case SUBD3:
397: case MULD3:
398: case DIVD3: switch (opernd_num) {
399: case 1:
400: case 2:
401: case 3: return (DOUBLE);
402: default: return (GARBAGE);
403: }
404: default: return (GARBAGE);
405: }
406: }
407:
408: /* fetch: Make a fetch from users space
409: * Returned data is static. Any changes are reflected in the actual code.
410: * BEWARE
411: */
412: ANYTYPE *fetch ()
413: {
414: extern char *pc;
415:
416: return ((ANYTYPE *)pc);
417: }
418:
419: /* reg_cont Return the contents of the specified register
420: */
421: reg_cont (reg)
422: int reg;
423: {
424: if (reg == PC) return ((int )pc);
425: if (reg == SP) return ((int ) ®s0t6[6]);
426: if (reg == FP) return (regs0t6[-2]);
427: if (reg == AP) return (regs0t6[-3]);
428: if (reg >= 0 && reg <= 6) return (regs0t6[reg]);
429: if (reg >= 7 && reg <= 11) return (regs7t11[reg]);
430: err ("Cannot deliver contents of register %d", reg);
431: return (NULL);
432: }
433:
434: /* addr_reg Return the stack address of the specified register
435: */
436: char *addr_reg (reg)
437: int reg;
438: {
439: extern long *regs0t6, *regs7t11;
440:
441: if (reg < 7)
442: return ((char *)(regs0t6 + reg));
443: return ((char *)(regs7t11 + reg - 7));
444: }
445:
446: /* load Load value into the address(es) pointed to by addr.
447: */
448: load (addr, type, valuel, valuer)
449: char *addr;
450: int type;
451: long valuel;
452: long valuer;
453: {
454:
455: *(long *)addr = valuel;
456: if (type != QUAD)
457: return;
458: /* Must fix other half */
459: addr = addr == addr_reg(6) ? addr_reg(7) : addr + 4;
460: *(long *)addr = valuer;
461: }
462:
463: /* err
464: * The fault handler is confused. Give the user a message and continue.
465: * Probably to nausea, but we might have done the job.
466: */
467: /* VARARGS 1 */
468: err (fmt, a, b, c, d, e, f, g, h, i, j, k, l)
469: char *fmt;
470: {
471:
472: fprintf (stderr, "Fault handler: ");
473: fprintf (stderr, fmt, a, b, c, d, e, f, g, h, i, j, k, l);
474: putc ('\n', stderr);
475: fflush (stderr); /* Deliver message NOW! */
476: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.