MASM:强制不重定位 OFFSET 指令

MASM: Force OFFSET directive not to be relocated

我正在用 MASM 为 DOS 编写一个多模块装配项目。特别是,我正在尝试做一个非常简单的 D[​​=46=]-like OS,

我有几个段是这样定义的;

fileseg segment byte public 'code'
fileseg ends

memseg segment byte public 'code'
memseg ends

diskseg segment byte public 'code'
diskseg ends

bootseg segment byte public 'code'
bootseg ends

如您所见,它们都是字节对齐的(设计决策)。我还有一个包含上述所有细分的 cseg 组

cseg group fileseg, memseg, diskseg, bootseg, ...

所以当我想引用一个 public 标签时,无论它是否在不同的段中定义,我都会做类似的事情:

assume ds:cseg, es:cseg, ss:cseg

mov   si, offset label_in_this_segment
mov   bx, offset label_in_another_segment

它会在 cseg 中得到 'global' 偏移量,这是正确的。但是当你编写一些需要在固定位置执行的代码时,问题就出现了,作为引导扇区,在 0000:7C00h 执行(在我的例子中是 07C0:0)

所以我是这样写的:

bootseg segment byte public 'code'

; Want all data references to be relative to this segment, not CSEG
assume ds:bootseg

boot_start::

    jmp     start

    boot_data   t_bootloader_data <>        

start:

    ; make IP point to 0000h
    mov     ax, 07C0h
    push    ax
    mov     ax, offset ds:real_start ; "BAD" offset
    push    ax
    retf
real_start:        
    mov     ax, cs
    mov     ds, ax
    ...

我把 "BAD" 放在引号中是因为 MASM 实际上做得对,因为 bootseg 也是 cseg 组的一部分(因为 boot_data 它被其他一些段引用),所以链接器将 "real_start" 标记为可重定位。

所以问题是:我如何告诉汇编程序我希望 real_startbootseg 本身偏移?

我尝试了一些有效的技巧,比如让 bootseg 成为定义的第一个段,或者让它与段落对齐,但这些对我来说似乎有点老套。

我也尝试将 bootseg 置于 cseg 组之外,但同样,在将引导扇区写入磁盘之前,我需要从其他段引用 boot_data

您可以通过在符号前加上 <i>segment</i>: 来指定您希望 OFFSET 相对于哪个段。例如:

     mov     ax, offset bootseg:real_start

但是这不起作用,因为您使用的是 BYTE 对齐方式,所以该段有两个不同的起始地址。在实模式中,段必须是段落(16 字节)对齐的,因此链接器将开始 bootseg 舍入到最近的段落边界,然后调整任何偏移量以相对于新的起始地址。因为当 bootseg 实际加载并执行时,它实际上开始了一个段落边界,这意味着所有调整后的偏移量都不正确。

请注意,它不仅限于 OFFSET 指令,还包括 bootsegbootseg 内地址的任何绝对引用。你真的应该考虑让 bootseg PARA 对齐。

如果您仍想使用字节对齐,则需要自己计算偏移量:

     mov     ax, real_start - boot_start

请注意,由于您的目标是 80286 处理器,因此您可以改为这样做:

     push    real_start - boot_start

PUSH 立即指令是通过 80186 引入 x86 架构的。

如果您在 bootseg 中有这样的绝对引用:

mov  al, [boot_data.foo]

您需要将其更改为这样的内容,以便在执行引导扇区时偏移量正确:

mov  al, BYTE PTR ds:[boot_data.foo - boot_start]

最后你可以避免整个 PUSH/RETF 废话和代码 直接在 MASM 中像这样:

bootseg segment byte public 'code'

assume ds:bootseg

boot_start::

    jmp     start

    ; ...

start:

    jmp     real_start_abs
real_start:        
    mov     ax, cs
    mov     ds, ax

    ; ...

bootseg ENDS

bootseg_abs SEGMENT USE16 AT 07c0h
    ORG      (real_start - boot_start)
real_start_abs LABEL FAR
bootseg_abs ENDS