Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!usc!wuarchive!emory!tridom!atssc!fang!tarpit!bilver!dandrews From: dandrews@bilver.uucp (Dave Andrews) Newsgroups: comp.os.msdos.programmer Subject: Chaining interrupt handlers in Turbo C Keywords: TC TURBOC interrupt _chain_intr Message-ID: <1991Apr17.214023.22334@bilver.uucp> Date: 17 Apr 91 21:40:23 GMT Organization: W. J. Vermillion - Winter Park, FL Lines: 80 /* * A week or so ago, someone asked: * "how can I chain to the next interrupt handler in Turbo C?" * * Shortly after the post appeared, I needed the same function. * I see from _Undocumented DOS_ that MSC has the "_chain_intr" * builtin that does what you want. Unfortunately, TC doesn't * have an equivalent function (see _Undocumented DOS_ page 379). * * Here is my solution to the problem. It is not particularly * elegant, and is HIGHLY dependent on the code TC++ 1.0 generates * for INTERRUPT function epilogs. I've only tested it on a * '286 machine in the large model. YMMV and all that. * * Just before exit, the stack contains saved registers, CS/IP * and flags. I shift the stack, duplicating the CS/IP/flags * (6 bytes) and modify the topmost CS/IP to point to the previous * interrupt handler. Then I let TC's generated exit code restore * registers and IRET to the previous handler. When the previous * handler does its own IRET, it returns control to my original * caller. * * Note that the chain_intr macro destroys a number of registers * rather indiscriminately. Use chain_intr() just before your * INTERRUPT function executes a return. The stack should be * the same as it was on entry, i.e. don't use chain_intr() * inside a subordinate function call. * * Hope this helps. * * - David Andrews bilver!dandrews */ #include #define chain_intr(old) \ asm { \ push ds; /* Need DS for access to "old" pointer */ \ mov si,sp; /* -> current top of stack */ \ sub sp,6; /* -> new top of stack after shift */ \ mov di,ss; /* Setup DS, ES for move */ \ mov ds,di; /* DS:SI is source of move */ \ mov es,di; /* */ \ mov di,sp; /* ES:DI is destination of move */ \ mov cx,12; /* Moving 24 bytes (12 words) */ \ rep movsw; /* Shift stack 6 bytes */ \ pop ds; /* Restore DS */ \ cli ; /* Protect stack */ \ add sp,22; /* Zap IRET address to prior handler */ \ push DWORD PTR _##old; /* " */ \ sub sp,18; /* " */ \ sti ; /* Unprotect stack */ \ } void interrupt (*oldfunc) (); /* Object of getvect(), setvect() */ void interrupt first () { /* Sample handler - installed first */ puts("First"); } void interrupt second () { /* Sample handler - installed second */ puts("Second"); chain_intr (oldfunc); /* Example use of "chain_intr" macro */ } void main() { setvect(255, first); /* Install "first" as INT 255 handler */ oldfunc = getvect(255); /* getvect() its address */ setvect(255, second); /* Install the second INT 255 handler */ geninterrupt(255); /* Call the second INT 255 handler. */ /* It will execute and pass control to */ /* the previous handler (whose address */ /* is stored in "oldfunc"). Expected */ /* output is: */ /* Second */ /* First */ };