Solved

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

Posted on 2003-10-31
10
294 Views
Last Modified: 2010-04-15
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:");
    scanf("%d",&a);
    printf("Number 2:");
    scanf("%d",&b);
    c = a / b;
    printf("%d / %d = %d\n",a,b,c);
    main();
}

void interrupt divide0()
{
    printf("Error: Divide by Zero\n");
    (*oldhandle)();
}

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
0
Comment
Question by:ninja_turtle_2003
  • 6
  • 2
10 Comments
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
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.
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
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.
0
 

Author Comment

by:ninja_turtle_2003
Comment Utility
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.
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
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:

--------8<--------
/* 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)(); */
}
--------8<--------
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 

Author Comment

by:ninja_turtle_2003
Comment Utility
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?
0
 
LVL 17

Accepted Solution

by:
rstaveley earned 50 total points
Comment Utility
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.
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
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):

--------8<--------
#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 */
      atexit(myexit);

      printf("%d/%d is %d\n"
            ,x,y
            ,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()
{
      setvect(0x00,oldhandler);
      puts("Old handler restored\n");
}
--------8<--------

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.
0
 
LVL 17

Expert Comment

by:rstaveley
Comment Utility
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!
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Preface I don't like visual development tools that are supposed to write a program for me. Even if it is Xcode and I can use Interface Builder. Yes, it is a perfect tool and has helped me a lot, mainly, in the beginning, when my programs were small…
Windows programmers of the C/C++ variety, how many of you realise that since Window 9x Microsoft has been lying to you about what constitutes Unicode (http://en.wikipedia.org/wiki/Unicode)? They will have you believe that Unicode requires you to use…
The goal of this video is to provide viewers with basic examples to understand and use pointers in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use structures in the C programming language.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now