dandrews@bilver.uucp (Dave Andrews) (04/18/91)
/* * 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 <dos.h> #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 */ };
few@gupta.portal.com (Frank Whaley) (04/25/91)
In article <1991Apr17.214023.22334@bilver.uucp> dandrews@bilver.uucp (Dave Andrews) writes: > * A week or so ago, someone asked: > * "how can I chain to the next interrupt handler in Turbo C?" And Dave responded with a very interesting version of chain_intr(). I never would have thought of his method. Unfortunately there are a couple of problems. Interrupts are improperly chained if the interrupt handler function has local variables. Turbo C (2.0, I can't speak for TC++ or BC++) only sets the BP register when the interrupt function uses local variables, so it cannot be used in all cases to unroll the stack. The best way to write handlers with local data: void interrupt handler() { if ( somethingWeDo ) doIt(); else chain_intr(original); } Dave's macro can only be used before a return, either stated or implied. So: if ( notForMe ) { chain_intr(original); return; /* required */ } ... I've attached a new version of what I use. It still suffers from not working in handlers with local variables, but it can be called anywhere and is implemented as a function. PAGE 60, 132 TITLE chain 24-Apr-91 Chain interrupt | ;-----------------------------------------------------------------------| ; | ; Turbo C Library -- ChainIntr | ; Chain interrupt | ; | ;-----------------------------------------------------------------------| ; sizes LCODE = 0 IFDEF __MEDIUM__ LCODE = 1 ENDIF IFDEF __LARGE__ LCODE = 1 ENDIF IFDEF __HUGE__ LCODE = 1 ENDIF ; Code Segment IF LCODE CHAIN_TEXT Segment Byte Public 'CODE' Assume CS:CHAIN_TEXT ELSE _TEXT Segment Byte Public 'CODE' Assume CS:_TEXT ENDIF PAGE ;-----------------------------------------------------------------------| ; Chain interrupt | ; | ; SYNOPSIS | ; void ChainIntr(void (interrupt *target)()); | ; | ; DESCRIPTION | ; Chain to another interrupt handler, passing original | ; (or possibly modified) register values. | ; | ; RETURNS | ; This function returns to the original source of the | ; interrupt. | ; | ;-----------------------------------------------------------------------| Public _ChainIntr IF LCODE _ChainIntr Proc Far ELSE _ChainIntr Proc Near ENDIF POP AX ; clear return addr IF LCODE POP AX ENDIF POP BX ; get target addr POP AX MOV BP,SP ; store target addr XCHG AX,[BP + 16] XCHG BX,[BP + 14] POP BP ; restore saved registers POP DI POP SI POP DS POP ES POP DX POP CX RETF ; return to target _ChainIntr EndP IF LCODE CHAIN_TEXT EndS ELSE _TEXT EndS ENDIF END ; of chain.asm -- Frank Whaley Software Engineer Gupta Technologies few@gupta.com
dandrews@bilver.uucp (Dave Andrews) (04/29/91)
In article <1991Apr24.204727.27737@gupta.portal.com> few@gupta.portal.com (Frank Whaley) writes: >In article <1991Apr17.214023.22334@bilver.uucp> dandrews@bilver.uucp (Dave Andrews) writes: > >> * A week or so ago, someone asked: >> * "how can I chain to the next interrupt handler in Turbo C?" > >And Dave responded with a very interesting version of chain_intr(). I >never would have thought of his method. Unfortunately there are a >couple of problems. > > Interrupts are improperly chained if the interrupt handler function > has local variables. Turbo C (2.0, I can't speak for TC++ or BC++) > only sets the BP register when the interrupt function uses local > variables, so it cannot be used in all cases to unroll the stack. Indeed, this is a problem, but I only (obviously!) tested with global variables. I generally hesitate to use local variables in a procedure of type INTERRUPT, because I don't know how big a stack is being provided by the caller. Outside of this problem, there is a "feature" in the code that found its way into the posting. I don't move enough of the stack (to quote Maxwell Smart, "Missed it by THAT much"). Replace: mov cx,12 by: mov cx,13 Frank, is your ChainIntr function equivalent to the MSC _chain_intr builtin? Thanks for your comments and code! - David Andrews bilver!dandrews