Solved

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

Posted on 2003-10-31
10
300 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
ID: 9658018
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
ID: 9658075
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
ID: 9659881
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
Optimizing Cloud Backup for Low Bandwidth

With cloud storage prices going down a growing number of SMBs start to use it for backup storage. Unfortunately, business data volume rarely fits the average Internet speed. This article provides an overview of main Internet speed challenges and reveals backup best practices.

 
LVL 17

Expert Comment

by:rstaveley
ID: 9662634
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
 

Author Comment

by:ninja_turtle_2003
ID: 9669908
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
ID: 9670171
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
ID: 9670560
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
ID: 9676141
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

Best Practices: Disaster Recovery Testing

Besides backup, any IT division should have a disaster recovery plan. You will find a few tips below relating to the development of such a plan and to what issues one should pay special attention in the course of backup planning.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

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…
This is a short and sweet, but (hopefully) to the point article. There seems to be some fundamental misunderstanding about the function prototype for the "main" function in C and C++, more specifically what type this function should return. I see so…
The goal of this video is to provide viewers with basic examples to understand how to use strings and some functions related to them in the C programming language.
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use while-loops in the C programming language.

778 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