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_start
从 bootseg
本身偏移?
我尝试了一些有效的技巧,比如让 bootseg
成为定义的第一个段,或者让它与段落对齐,但这些对我来说似乎有点老套。
我也尝试将 bootseg
置于 cseg 组之外,但同样,在将引导扇区写入磁盘之前,我需要从其他段引用 boot_data
。
您可以通过在符号前加上 <i>segment</i>:
来指定您希望 OFFSET 相对于哪个段。例如:
mov ax, offset bootseg:real_start
但是这不起作用,因为您使用的是 BYTE 对齐方式,所以该段有两个不同的起始地址。在实模式中,段必须是段落(16 字节)对齐的,因此链接器将开始 bootseg
舍入到最近的段落边界,然后调整任何偏移量以相对于新的起始地址。因为当 bootseg 实际加载并执行时,它实际上开始了一个段落边界,这意味着所有调整后的偏移量都不正确。
请注意,它不仅限于 OFFSET 指令,还包括 bootseg
对 bootseg
内地址的任何绝对引用。你真的应该考虑让 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
我正在用 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_start
从 bootseg
本身偏移?
我尝试了一些有效的技巧,比如让 bootseg
成为定义的第一个段,或者让它与段落对齐,但这些对我来说似乎有点老套。
我也尝试将 bootseg
置于 cseg 组之外,但同样,在将引导扇区写入磁盘之前,我需要从其他段引用 boot_data
。
您可以通过在符号前加上 <i>segment</i>:
来指定您希望 OFFSET 相对于哪个段。例如:
mov ax, offset bootseg:real_start
但是这不起作用,因为您使用的是 BYTE 对齐方式,所以该段有两个不同的起始地址。在实模式中,段必须是段落(16 字节)对齐的,因此链接器将开始 bootseg
舍入到最近的段落边界,然后调整任何偏移量以相对于新的起始地址。因为当 bootseg 实际加载并执行时,它实际上开始了一个段落边界,这意味着所有调整后的偏移量都不正确。
请注意,它不仅限于 OFFSET 指令,还包括 bootseg
对 bootseg
内地址的任何绝对引用。你真的应该考虑让 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 废话和代码
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