如何在 MASM 中编写远绝对 JMP/CALL 指令?

Ros*_*dge 5 x86 assembly masm

如何使用 MASM 编写远绝对 JMP 或 CALL 指令?具体来说,我如何让它使用 EA 和 CA 操作码发出这些指令,而不是使用 DB 或其他数据指令手动发出它们?

例如,考虑跳转到引导扇区中 FFFF:0000 处的 BIOS 重置入口点的情况。如果我使用 NASM,我可以以一种显而易见的方式在一条指令中对此进行编码:

jmp 0xffff:0
Run Code Online (Sandbox Code Playgroud)

使用 GNU 汇编器,语法不太明显,但以下内容可以完成这项工作:

jmp 0xffff, 0
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试使用 MASM 的明显解决方案时:

jmp 0ffffh:0
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

t206b.asm(3) : error A2096:segment, group, or segment register expected
Run Code Online (Sandbox Code Playgroud)

我试图避免的解决方法

我可以在 MASM 中使用许多可能的解决方法,例如以下任何一种:

手动组装指令,手动发出机器代码:

jmp 0xffff:0
Run Code Online (Sandbox Code Playgroud)

使用远间接跳转:

jmp 0xffff, 0
Run Code Online (Sandbox Code Playgroud)

或者将地址压入堆栈并使用远 RET 指令“返回”它:

jmp 0ffffh:0
Run Code Online (Sandbox Code Playgroud)

但是无论如何我可以使用实际的 JMP 指令并让 MASM 生成正确的操作码 (EA)?

Ros*_*dge 6

有一种方法可以做到,但您需要使用 MASM 的/omf开关,以便它生成 OMF 格式的目标文件。这意味着目标文件需要与 OMF 兼容的链接器链接,例如 Microsoft 的旧分段链接器(而不是他们当前的 32 位链接器)。

为此,您需要使用 MASM 的一个很少使用且不太了解的特性,即SEGMENT指令的属性。该属性告诉链接器该段位于内存中的固定段落地址,如. 它还告诉链接器丢弃该段,这意味着不使用该段的内容,只使用它的标签。这也是必须使用开关的原因。MASM 的默认目标文件格式 PECOFF 不支持此功能。AT addressATaddress/omf

AT属性为您提供了我们要跳转到的地址的段部分。要获得偏移部分,您需要做的就是在段内使用 LABEL 指令。结合 ORG 指令,您可以在特定段中的特定偏移量处创建标签。然后您需要做的就是在 JMP 或 CALL 指令中使用这个标签。

例如,如果您想跳转到 BIOS 重置例程,您可以执行以下操作:

bios_reset_seg SEGMENT USE16 AT 0ffffh
bios_reset LABEL FAR
bios_reset_seg ENDS

_TEXT SEGMENT USE16 'CODE'
    jmp bios_reset
_TEXT ENDS
Run Code Online (Sandbox Code Playgroud)

或者,如果您想调用入口点位于 0000:7E00 的引导加载程序的第二阶段部分:

zero_seg SEGMENT USE16 AT 0
    ORG 7e00h
second_stage LABEL FAR
zero_seg ENDS

_TEXT SEGMENT USE16 'CODE'
    call second_stage
_TEXT ENDS
Run Code Online (Sandbox Code Playgroud)