我需要生成一条远跳指令来跳到另一个ISR(中断服务例程)。我正在开发一个32位的FreeDOS应用程序。
在阅读了OW手册(cguide.pdf和clr.pdf)后,我找到了两种方法,使在没有任何警告或错误的情况下成功编译。
/* 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*/
}
我发现的另一种方法是:
/* 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);
现在我的问题是,在以上两种方式中,哪个更正确或更好?有什么想法或意见吗?
有其他方法可以做到这一点吗?
interrupt
函数总是很远。
就指令本身而言,手动构建的远跳转似乎是正确的,然而,我敢打赌,简单的跳转(而不是调用)不会删除new08()
之前在其序言时保存在堆栈上的内容(这可能会有很多寄存器,最重要的是,还有一个隐藏的返回地址,old08()
必须返回到该地址!)。
为什么这么有创意?
#include <dos.h>
void _chain_intr( void (__interrupt __far *func)(void) );
此函数可用于跳转到链中的另一个中断处理程序。此函数永远不会返回。它弹出interrupt关键字保存的所有寄存器,并跳到处理程序。当func指定的中断处理程序接收到控制时,堆栈和寄存器看起来就像中断刚刚发生一样。
此函数只能在用interrupt关键字声明的函数中使用。
跳转而不是调用的优势在irq处理程序中并不明显,但在软件中断处理程序中肯定是如此。链中的下一个软件中断处理程序希望cpu寄存器包含一些信息,例如传递给它的参数,因此在跳到下一个处理程序chaintr之前,将恢复所有cpu寄存器,就好像下一个处理器直接接收控制一样。
我可能会写这样的东西:
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 */
}
我记得32位模式将把选择器作为32位单元来推送和弹出,即使只使用低16位。