您想要完成什么并不完全清楚。似乎您想要一个中断处理程序,默认情况下执行 iret 而没有其他推送和弹出。
海合会
使用 GCC(不带 NASM)可能会出现这样的情况:
/* Make C extern declarations of the ISR entry points */
extern void isr_test1(void);
extern void isr_test2(void);
/* Define a do nothing ISR stub */
__asm__(".global isr_test1\n"
"isr_test1:\n\t"
/* Other stuff here */
"iret");
/* Define an ISR stub that makes a call to a C function */
__asm__(".global isr_test2\n"
"isr_test2:\n\t"
"cld\n\t" /* Set direction flag forward for C functions */
"pusha\n\t" /* Save all the registers */
/* Other stuff here */
"call isr_test2_handler\n\t"
"popa\n\t" /* Restore all the registers */
"iret");
void isr_test2_handler(void)
{
return;
}
GCC 中的基本__asm__ 语句可以放在函数之外。我们为我们的中断服务例程 (ISR) 定义标签,并使用 .globl 使它们在外部可见(您可能不需要全局可见性,但无论如何我都会展示它)。
我创建了几个示例中断服务例程。一个只做iret,另一个对C 处理程序进行函数调用。我们保存所有寄存器并在之后恢复它们。 C 函数需要将方向标志设置为向前,因此在调用 C 函数之前我们需要一个CLD。此示例代码适用于 32 位目标。 64位可以通过单独保存寄存器而不是使用PUSHA和POPA来完成。
注意:如果在 Windows 上使用 GCC,则汇编块内部的函数名称可能需要前面加上_(下划线)。它看起来像:
/* Make C extern declarations of the ISR entry points */
extern void isr_test1(void);
extern void isr_test2(void);
/* Define a do nothing ISR stub */
__asm__(".global _isr_test1\n"
"_isr_test1:\n\t"
/* Other stuff here */
"iret");
/* Define an ISR stub that makes a call to a C function */
__asm__(".global _isr_test2\n"
"_isr_test2:\n\t"
"cld\n\t" /* Set direction flag forward for C functions */
"pusha\n\t" /* Save all the registers */
/* Other stuff here */
"call _isr_test2_handler\n\t"
"popa\n\t" /* Restore all the registers */
"iret");
void isr_test2_handler(void)
{
return;
}
MSVC/MSVC++
Microsoft 的C/C++ 编译器支持函数的naked 属性。他们将此属性描述为:
裸存储类属性是 Microsoft 对 C 语言的特定扩展。对于使用裸存储类属性声明的函数,编译器生成的代码没有 prolog 和 epilog 代码。您可以使用此功能使用内联汇编代码编写自己的序言/结语代码序列。裸函数在编写虚拟设备驱动程序时特别有用。
一个示例中断服务例程可以这样完成:
__declspec(naked) int isr_test(void)
{
/* Function body */
__asm { iret };
}
您需要处理保存和恢复寄存器的问题,以与上述 GCC 示例类似的方式自行设置方向标志。
GCC 7.x+ 在 x86/x86-64 目标上引入了中断属性
在 GCC 7.0+ 上,您现在可以在函数上使用 __attribute__((interrupt))。这个属性最近才在 x86 和 x86-64 目标上得到支持:
中断
使用该属性来指示指定的函数是中断处理程序还是异常处理程序(取决于传递给函数的参数,进一步解释)。当此属性存在时,编译器生成适用于中断处理程序的函数进入和退出序列。 IRET 指令,而不是 RET 指令,用于从中断处理程序返回。除由 IRET 指令恢复的 EFLAGS 寄存器外,所有寄存器都由编译器保留。由于 GCC 不保留 MPX、SSE、MMX 或 x87 状态,因此应使用 GCC 选项 -mgeneral-regs-only 来编译中断和异常处理程序。
此方法仍有不足之处。如果您希望您的 C 代码访问在中断时出现的寄存器的内容,那么目前没有可靠 的方法可以使用这种机制来实现它.如果您正在编写软件中断并且需要访问寄存器以确定要采取的操作(即:Linux 上的int 0x80),这将非常方便。另一个示例是允许中断将所有寄存器内容转储到显示器以进行调试。