ID Number: Q73153
5.00 5.10 6.00 6.00a 6.00ax | 5.10 6.00 6.00a
MS-DOS | OS/2
Summary:
In Microsoft C versions 5.1, 6.0, 6.0a, and 6.0ax, 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 (Int 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:
run-time error R6003
- integer divide by 0
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 Int 0.
This can be done using the _dos_setvect() run-time function for
MS-DOS, or the DosSetVec() API function for OS/2.
More Information:
The code example below demonstrates how this is done in Microsoft C or
Microsoft QuickC. The run time will install a default Int 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 under 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.
Sample Code
-----------
/* Compile options needed for DOS : /G2 /DDOS
Compile options needed for OS/2: /G2 /DOS2
*/
#ifdef OS2
#define INCL_DOSMISC
#include <os2.h>
#endif
#include <stdio.h>
#include <dos.h>
void main(void);
void _far _cdecl _interrupt ZeroDivTrap(unsigned int,
unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int);
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)( void );
OldZeroDiv = _dos_getvect(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 int _es,
unsigned int _ds, unsigned int _di, unsigned int _si,
unsigned int _bp, unsigned int _sp, unsigned int _bx,
unsigned int _dx, unsigned int _cx, unsigned int _ax,
unsigned int _ip, unsigned int _cs, unsigned int _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 reference words: 5.10 6.00 6.00a 6.00ax 2.0 2.00 2.01 2.5
2.50 2.51