基于寄存器值的汇编调用中断

Assembly call interrupt based on register value

我想在 NASM 中有一个中断,它调用的不是硬编码中断而是 int。在寄存器中。 举个例子:

mov al, 0x10
int al    ; you can't do this for some reason

因此,如果我将 0x10 存储在寄存器 al 中,那么我可以根据该寄存器中的内容调用中断。

有什么办法可以做到这一点吗?

一般情况下没有好的/简单的选项,不要这样做。你需要自修改代码,或者跳转table每个选项,或条件分支链,如果只有几个可能的值。

(但是,如果您只需要它在实模式下工作,请参阅 Martin 的回答;使用 pushfcall far [ivt_entry] 模拟 int

如果你想做一个包装函数,不要;使它成为一个宏,这样它就可以将中断号作为常量内联。或者为您要使用的每个中断号制作单独的包装器。

Is there any way I could do this?

16位"Real Mode"无自修改码:

大多数 DOS 的 C 编译器都提供了一个库函数,允许执行与 int al.

等价的操作

其工作方式如下:

在实模式下,int 指令等于 pushf 后跟远 call 到中断处理程序。

然而,"far" call 只是将下一条指令的 "far" 地址(csip)压入堆栈,并且进行跳跃。 (一个 "near" 调用只压入 ip。)一个 retf 将从堆栈中弹出 ipcs 并跳转到该地址。

中断处理程序的地址存储在地址0:(4*n)。

因此要进入中断,首先执行以下代码:

  pushf
  push cs       # Leave this line out if "call helper" is a "far" call
  call helper

当进入函数helper时,栈是这样的:

Address (IP) of the instruction after "call helper"
Segment (CS) of the program
Flags
...

这三个元素在 int 指令之后在堆栈上。

程序 helper 看起来像这样。

helper:
    # Calculate BX = 4*AX
    # (This can be done with less instructions on a modern CPU)
  mov bl,al
  mov bh,0
  add bx,bx
  add bx,bx
    # Set DS to 0
  xor ax,ax
  mov ds,ax
    # Push the segment part of the interrupt handler address
    # to the stack
  push word [bx+4]
    # Push the offset part
  push word [bx]
    # Load all registers with the desired values
  # TODO: Ensure all registers have the correct values
    # Enter the interrupt
  retf

retf 之前,堆栈将如下所示:

Address (IP) of the interrupt routine
Segment (CS) of the interrupt routine
Address (IP) of the instruction after "call helper"
Segment (CS) of the program
Flags
...

retf 指令的行为方式与前两个字已被 "far" call 指令压入相同:它将从堆栈中删除前两个字并跳转到这两个词所描述的地址 - 这意味着:进入中断处理程序。

在中断处理程序结束时,最后3个字将从堆栈中弹出,并在call helper之后的指令处继续执行。

16位"Real Mode"中自修改代码:

这很简单:

    # AL contains the interrupt number
    # Modify the "int" instruction, so "int 42" becomes
    # "int 31" if the value of AL is 31.
  mov cs:[theIntInstruction+1], al
    # Load all registers with the desired values
  # TODO: Ensure all registers have the correct values
    # Now perform the "int" instruction
theIntInstruction:
  int 42

自修改代码可能会产生负面影响。这意味着可能会有问题...

In(16 位或 32 位)"Protected Mode":

根据内存保护设置,您有机会写入 "executable" 内存。在这种情况下,您可以使用自修改代码。

如果您无法使用自修改代码,则无法执行与 int al 等效的操作,除非您想执行以下操作:

performInt0:
    int 0
    ret
performInt1:
    int 1
    ret
performInt2:
    int 2
    ret
...
performInt255:
    int 255
    ret

... 然后对所需标签执行 call

这当然总是可能的。