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

How to code a far absolute JMP/CALL instruction in MASM?

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

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

jmp 0xffff:0

在 GNU assembler 中,语法不太明显,但以下内容可以完成工作:

jmp 0xffff, 0

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

jmp 0ffffh:0

我收到以下错误:

t206b.asm(3) : error A2096:segment, group, or segment register expected

我试图避免的解决方法

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

手动 assemble 指令,手动发出机器码:

    DB 0EAh, 0, 0, 0FFh, 0FFh

使用远间接跳转:

bios_reset DD 0ffff0000h
    ...
    jmp bios_reset   ; FF 2E opcode: indirect far jump

或者将地址压入栈中,用far RET指令"return"给它:

    push 0ffffh
    push 0
    retf

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

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

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

AT属性给出了我们要跳转到的地址的you段部分。要获得偏移部分,您需要做的就是在段内使用 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

或者如果你想调用入口点在 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

我一直在用 MASM 6.15 测试这段代码,我想这对你有帮助。

    .model small
    .386
    .stack 100h

    .DATA
     Message    DB  'hello','$'
     JMPPOS DB  78h,56h,34h,12h  ;the address to be jumped to  1234:5678

    .code
    .startup

     JMP  dword  ptr [JMPPOS]

    .exit
    END

您是否考虑过使用 __emit 伪指令?

https://msdn.microsoft.com/en-us/library/1b80826t.aspx

我曾经不得不使用内联汇编程序来编写远跳代码。我不记得远跳的操作码,但你可以这样做

__emit      0eah
__emit      0
__emit      0
__emit      0
__emit      0
__emit      8h
__emit      0