How to Trap Integer Divide-By-Zero Exceptions in CID: Q73153
|
In Microsoft C versions 5.1 and later, when performing integer math it is possible to generate an exception by attempting to divide an integer by zero. Since the result of a division by zero operation is undefined, Intel sets aside an interrupt (Interrupt 0) for the purpose of indicating that a divide-by-zero exception has occurred. In the default configuration, the run-time library shipped with Microsoft language products will issue the following error and terminate the program:
Depending on your program's requirements, this may be undesirable behavior. If you want to trap this error and handle it in a more appropriate manner, you need to set up an exception handler for Interrupt 0. This can be done using the _dos_setvect() run-time function for MS-DOS, or the DosSetVec() API function for OS/2.run-time error R6003 - integer divide by 0
The code example below demonstrates how this is done in Microsoft C or
Microsoft QuickC. The run time will install a default Interrupt 0
handler during execution of the startup code (see CRT0DAT.ASM for
specifics). If you want to install a different handler, you should
save the original interrupt vector so that you can restore it as part
of the cleanup procedure. With the run-time library in MS-DOS, this is
done using the _dos_getvect() function. Using the OS/2 API function
DosSetVec(), one of the arguments you pass is the address of where you
want the original function pointer stored.
The actual function that handles the error must be declared with the
_interrupt keyword. Doing so causes the compiler to save all the
registers on function entry, restore them when the function
terminates, and issue an "iret" instruction to return to the calling
procedure. For more information on the _interrupt keyword, see the
online help.
Inside the exception handler, you have a couple of choices on how to
handle the error: you can either terminate the process or ignore the
error and continue. If you decide to terminate the process, remember
to call one of the run-time exit routines to shut down the run time
[exit(), _exit(), _cexit(), or _c_exit()]. If you decide to ignore the
error and continue, you must increment CS:IP to skip over the
instruction that caused the error. This is also illustrated below in
the sample program.
/* Compile options needed for DOS : /G2 /DDOS
Compile options needed for OS/2: /G2 /DOS2
*/
#ifdef OS2
#define INCL_DOSMISC
#include <os2.h>
#endif
#pragma check_stack( off )
#pragma check_pointer( off )
#include <dos.h>
#include <stdio.h>
void main(void);
void _far _cdecl _interrupt ZeroDivTrap(unsigned, unsigned, unsigned,
unsigned, unsigned, unsigned,
unsigned, unsigned, unsigned,
unsigned, unsigned, unsigned,
unsigned);
int arg1 = 10;
int arg2 = 0;
int arg3;
int rc; // return codes...
void main(void)
{
#ifdef DOS
// For MS-DOS, first retrieve the old handler,
// then set up the new one.
void (_interrupt _far *OldZeroDiv)();
OldZeroDiv = _dos_getvect((unsigned)0x00);
_dos_setvect(0x00, ZeroDivTrap);
#endif
#ifdef OS2
// For OS/2, the API will take care of both for us.
PFN OldZeroDiv;
PFN NewZeroDiv;
rc = DosSetVec(VECTOR_DIVIDE_BY_ZERO,
(PFN)ZeroDivTrap, &OldZeroDiv);
if (rc != 0)
printf("rc: %d - DosSetvec() Failed...\n", rc);
#endif
// Now let's try to generate an error. This should do it:
arg3 = arg1 / arg2;
// Just to make sure we are back where we started, print a message.
printf("Back in the main code...\n");
// Clean up after ourselves.
#ifdef DOS
_dos_setvect(0x00, OldZeroDiv);
#endif
#ifdef OS2
rc = DosSetVec(VECTOR_DIVIDE_BY_ZERO, OldZeroDiv, &NewZeroDiv);
if (rc != 0)
printf("rc: %d - DosSetvec() Failed...\n", rc);
#endif
// All done!
}
void _far _cdecl _interrupt ZeroDivTrap(unsigned _es,
unsigned _ds, unsigned _di, unsigned _si,
unsigned _bp, unsigned _sp, unsigned _bx,
unsigned _dx, unsigned _cx, unsigned _ax,
unsigned _ip, unsigned _cs, unsigned _flags)
{
char chAddrByte;
printf("This is from inside the Exception Handler...\n");
// If we want to terminate the program, do it now. Remember to
// call one of the exit routines to shut down the run time
// [for example, exit(), _exit(), _cexit(), or _c_exit()].
// On the other hand, if you want to continue (knowing of course
// that the results are incorrect for the divide operation), you
// have to determine the size of the instruction that caused the
// exception and jump past it. This is done by examining the mod
// and r/m bits in the second byte of the op code and incrementing
// the IP register by the required amount. See the MASM or Intel
// docs for more information.
chAddrByte = *(char far *)(((unsigned long)_cs << 16) + _ip + 1);
chAddrByte &= 0xC7; // Mask off unneeded bits.
if (chAddrByte == 6) // If mod = 0 and r/m = 6, then
_ip += 4; // opcode is 4 bytes.
else
switch (chAddrByte >> 6) // Else, we look at mod only.
{
case 0:
case 3:
_ip += 2;
break;
case 1:
_ip += 3;
break;
case 2:
_ip += 4;
break;
}
}
Additional query words: kbinf 1.00 1.50 6.00 6.00a 6.00ax 7.00
Keywords : kb16bitonly
Version :
Platform :
Issue type :
Last Reviewed: August 8, 1999