0

I need to generate a far jump instruction to jump to another ISR(Interrupt Service Routine). I'm developing a 32-bit FreeDOS application.

After reading OW manuals(cguide.pdf and clr.pdf), I figured out two ways that compiled successfully w/o any warning or error.

    /* Code Snippet #1 */

    #pragma aux old08 aborts ;
    void (__interrupt __far *old08)(void);      // function pointer declaration


    void __interrupt __far new08(void) {

           /* Do some processing here ... */

           (*old08)();  /* OW will now generate a jump inst. instead of call*/
     }

The other approach that I figured out is:

      /* Code Snippet #2 */

      static void jumpToOld08(void);         
      # pragma aux jumpToOld08 = \
             ".686p"     \       
             "                DB      0xEA"  \          
             "off_old08       DD      0"     \               
             "sel_old08       DW      0"     ;             


      void __interrupt __far new08(void){

               /* Do some processing here ... */

               jumpToOld08();   
      }

      extern unsigned short sel_old08;
      extern unsigned int off_old08;

      sel_old08 = ( __segment )FP_SEG(old08);
      off_old08 = FP_OFF(old08);        

Now my question is which of the above two ways is more correct or better? Any ideas or opinions?

Are there any other ways to accomplish this?

jacks
  • 294
  • 3
  • 15

3 Answers3

2

interrupt functions are always far.

Your manually constructed far jump appears correct as far as the instruction itself is concerned, however, I bet, simply jumping (instead of calling) won't remove the stuff previously saved by new08() on the stack at its prologue (and that's potentially a lot of registers, and most importantly, there's also the return address buried to which your old08() has to return to!).

Why so inventive?

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • Yup... I missed that point. Thanks :) __interrupt functions of OW, save all the CPU registers on stack, and if I then jump to some other ISR, then after IRET is executed, the CPU will try to pop, EIP, CS and EFLAGS regs in order. The old08() will *not* emit any push, pops of return addresses as far as Code Snippet#1 is concerned, it will simply emit jump instruction. The problem is that there will be wrong values of EIP, CS and EFLAGS registers if I would simply jump to original ISR instead of calling it. That can really create a mess :( – jacks Jul 18 '12 at 10:59
  • Does it mean that we can't really emit jump instructions in OW C w/o messing up? or we *should not* use jump in OW C. – jacks Jul 18 '12 at 11:01
  • 1
    You can simulate the `int` instruction to transfer control to `old08()` (`push eflags` + `far call` or `push eflags` + `push cs` + `push return offset` + `far jmp`), but why would you want that? What's wrong with just calling `old08()`? – Alexey Frunze Jul 18 '12 at 11:15
  • Actually at first I thought that using jump rather than call would provide a neat code, as I do not need to actually take control back in new08( ), but it now turns out that it would create a more complex code instead. Well, thanks for ur suggestion to simulate 'int'. :) – jacks Jul 18 '12 at 12:47
0
    #include <dos.h>
    void _chain_intr( void (__interrupt __far *func)(void) );

This function can be used to jump to another interrupt handler in the chain. This function never returns. It pops off all the registers saved by the interrupt keyword and jumps to the handler. When the interrupt handler designated by func receives control, the stack and registers appear as though the interrupt just occurred.

This function can only be used within a function declared with a interrupt keyword.

The advantage of jumping rather than calling is not obvious in irq handlers, but definitely for software interrupt handlers. The next software interrupt handler in the chain expects cpu register to contain some info eg parameters passed to it, so before jumping to next handler chainintr restores all cpu registers as if the next handler directly recieves the control.

jacks
  • 294
  • 3
  • 15
0

I'd probably write something like this:

void far_jump (uint32_t offset, uint16_t selector)
{
    /* remove the (callee's) stack frame including the return address by manipulating esp & ebp */

    /* the top of the stack now points to offset:selector */

    _asm
    {
        retf    ; or whatever the asm syntax dictates
    }

    /* esp & ebp will now point to the caller's stack frame */
}

As I recall 32-bit mode will push and pop selectors as 32-bit units even though only the low 16 bits are used.

Olof Forshell
  • 3,169
  • 22
  • 28