【问题标题】:MASM: Force OFFSET directive not to be relocatedMASM:强制 OFFSET 指令不重定位
【发布时间】:2018-04-12 12:37:21
【问题描述】:

我正在用 MASM for DOS 编写一个多模块组装项目。特别是,我正在尝试做一个非常简单的类似DOS的操作系统,

我有几个这样定义的段;

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, ...

所以当我想引用公共标签时,无论它是否在不同的段中定义,我都会这样做:

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

mov   si, offset label_in_this_segment
mov   bx, offset label_in_another_segment

它会在 cseg 中获得“全局”偏移量,这是正确的。但是当你编写一些需要在固定位置执行的代码时,问题就出现了,作为引导扇区,在 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

【问题讨论】:

  • 问题:bootseg 有两个值:开机时和跳远后。你可以做一个偏移差异。 (这在两个 cs 上都是有效且相同的)并使用这样的值。
  • 不要试图让 MASM 了解偏移量。如果这是正确的语法,也许可以做类似jmp 07C0h:(real_start - start) 的事情(而不是那种 push/push / retf 的东西。IDK 如果你在达到real_start 后不打算利用仍然在 AX 中的价值,为什么要使用 AX .)
  • @PeterCordes 它是为 .286 编译的,所以既不能写 jmp XXXX:XXXX 也不能推送立即值。
  • @Trap 80286 肯定有jmp far ptr16:16 指令。是的,您可以使用它。
  • 如果它解决了您的实际问题,请不要仔细研究您的问题,直接答案是使用OFFSET bootseg:real_start。至于如何让 MASM 生成到特定固定段和偏移量的远跳转,请参阅这个问题:stackoverflow.com/questions/32706833/…

标签: assembly dos masm


【解决方案1】:

您可以通过在符号前加上 <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 中编码 an absolute far JMP to a fixed segment,如下所示:

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

【讨论】:

  • 段前缀 'bootseg' 的作用与 'assume ds:bootseg' 和 ds:xxx_label 相同。都试过了,但都没有工作(产生相同的“无效”偏移)
  • 问题与 bootseg 是字节对齐段组的一部分有关。
猜你喜欢
  • 1970-01-01
  • 2017-05-15
  • 2019-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多