• C

Setting a handler for interrupts to over-ride the existing one (Turbo C - DOS)

Hi, I am trying to write a program that has it's own handler for interrupts.
For simplicity's sake, let's say the program is getting 2 numbers from a user, and dividing them. If a divide by 0 error occurs, the handler is called.
Here's the code I would use for that:

void interrupt (*oldhandle)();
void interrupt divide0();

void main()
    int a,b,c;
    oldhandle = getvect(0x00); /*0x00 is processor / 0 error*/
    setvect(0x00, divide0);
    printf("Number 1:");
    printf("Number 2:");
    c = a / b;
    printf("%d / %d = %d\n",a,b,c);

void interrupt divide0()
    printf("Error: Divide by Zero\n");

This code works fine if the 2 numbers are non-zero, and if number 1 is 0.
However, if number 2 is zero, divide0() is called, and the error is printed, but another interrupt handler is called afterwards, which prints Divide Error, and exits the program.
I think that this error is built into the operating system, and is the default handler for divide errors.
I want the program to continue, although this interrupt has occured.
How can I tell the system that the interrupt is completed and to move on?
Thanks in advance
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

The default handler returns control to DOS after printing "Divide by Zero". I'm not 100% sure about this (so be warned!), but I think you can get away with simply avoiding the call to the default handler, if you are using a 80386 or better. You probably ought to put disable() at the beginning of your ISR and enable() at the end.
You also ought to reinstate the original handler on exit! It isn't really OK calling main() recursively from main, because eventually you'll run out of stack space. May I suggest testing with a loop, which exits, when a and b meet a certain condition - e.g. they both -1. You might want to implement setvect(0x00,oldhandler) in an atexit handler.
ninja_turtle_2003Author Commented:
How would I avoid a call to the default handler? At the moment it seems to be using both my handler and this default one.
Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

Be warned that I'm not very confident about this. My ISRs were heavily plagiarised and it has been a long time since I did anything like this.

If I remember rightly, you can return OK from an 80386 or later INT 0 ISR, but 80286 or earlier had CS:IP pointing to the instruction that causes the exception. Because the ISR is a software exception and not therefore thrown by the PIC, there is no need to send an EOI to the PIC.

You'll need to experiment, and I'll defer to any other DOS dinosaurs out there :-)

Try this:

/* Avoid the call to the default handler - i.e. comment it out.
   I think you ought to disable interrupts during this ISR too,
   by putting a call to enable() at the beginning and disable()
   at the end. */
void interrupt divide0()
    puts("Error: Divide by Zero\n");
    /* (*oldhandle)(); */
ninja_turtle_2003Author Commented:
I tried doing that, but it caused another problem.
This time, when I put zero into the program, my error handler is called, so Error: Divide by Zero is printed, and the default error handle is not called.
However, the program seems to continually call the error handler, as it sits in a loop printing  Error: Divide by Zero.
Is there something I have to do to tell the system that I have handled the interrupt, and it can be cleared?
I got it wrong about the 80386 CS:IP, 80286 and 80386 push the CS:IP of the IDIV instruction and 8086 pushes the CS:IP of the following instruction. That means that you need to increment the pushed IP in your ISR to get it to progress beyond the IDIV instruction, when it IRETs.

If you are using TurboC from the Borland Museum, you'll need to get your hands on tasm.exe to do some assembler work. You want to increment the value of the CS:IP pushed on the stack to get it to return to the next instruction after the IDIV to prevent it repeatedly calling IDIV. I recommend compiling with the -S switch to see the assembly (.asm code) so that you can find the address of the CS:IP, which you need to increment to get your IRET to go to the right place. You'll find that your ISR pushes all of the registers. I debugger would be a fine thing for this so you can step through your ISR and see that it increments the right bit of the stack.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
This works with TurboC 2.01 from the Borland museum, using TASM from Borland C++ 4 for the inline assemler (an old disk I had):

#include <stdio.h>
#include <dos.h>

void interrupt (*oldhandler)();
void interrupt myhandler();
void myexit();

int main()
int x = 10,y = 0;

      puts("Testing INT0 handler\n");

      oldhandler = getvect(0x0);      /* Divide by zero default handler */
      setvect(0x00,myhandler);      /* Install replacement handler */

      printf("%d/%d is %d\n"
            ,x/y                  /* This assembles to IDIV DI */

      puts("Program terminated normally\n");

void interrupt myhandler()
      puts("My divide by zero handler: Caught exception!\n");
      asm      mov si,sp;

/* Make the return address skip past the opcodes for IDIV DI. We are messing with the IP pushed on the stack */

      asm      add word ptr [si+18],2;            /* Skip the IDIV DI instruction */

/* The result of the division is in AX (and remainder in DX). These were pushed on the stack by TurboC, because we are in an ISR */

      asm      mov word ptr [si+16],0ffffh;      /* AX = -1 */
      asm      mov word ptr [si+10],0ffffh;      /* DX = -1 */

void myexit()
      puts("Old handler restored\n");

You can see that it increments past the two opcodes generated for the division (IDIV DI) and pokes the value -1 into AX, taking an arbitrary application decision that you want a return value of -1 when you div by zero.

I hope this helps.
Be warned that the approach that I posted is grossly over-simplistic to be used in a real application. It assumes that every divide by zero is caused by a two opcode instruction.The example works with the stated compiler only because TurboC 2.01 happens to compile that code using a register variable for the operand and executes the instruction IDIV DI, when the divide by zero exception is thrown. If you increase the complexity of the code which generates the IDIV, you soon get a situation where IDIV uses an operand from the stack frame (an automatic variable) and you get a 3 opcode instruction like IDIV WORD PTR [BP-4].

It makes you appreciate that life was much easier implementing the divide0 handler for 8086. A 80286+ handler requires that you take the address of the opcodes from the stack and interpret the opcodes to find out how many are used in the instruction so that you can progress to the next instruction. Presumably this is something which Intel reckoned was a good thing to have to do in divide by zero handler, because your handler gets access to the actual registers and operand, which caused the divide by zero. Over in the EE Assembler TA they'd probably make easy work of this sort of thing!
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.