如何将 Turbo-C 远指针分配和取消引用转换为 x86 程序集?

How to convert Turbo-C far-pointer assignment and dereference to x86 assembly?

我要进行汇编测试,我有一个关于汇编指针的问题。我正在尝试做一个练习,但我无法解决它。

考虑 C 中的语句:

int x=100, y=200;
int far *ptx;
int far *pty;

假设指令已经执行:

ptx=&x;
pty=(int *)malloc(sizeof(int));   

我的问题是如何在汇编中对以下几点进行编码:

  1. ptx=pty
  2. *ptx=*pty

这些声明应该在全球范围内吗?如果是这样,C 变量的静态存储上将有 asm 标签。如果不是(函数内的局部变量),它们将在堆栈上并且 IDK 他们如何期望您知道它们将位于 BP 的偏移量。

无论哪种方式,它们都是 32 位的 seg:off(小端序,因此在低 16 位中偏移)远指针,因此将一个复制到另一个只是一个 4 字节的副本,您可以使用2 个整数加载 + 存储。

指针变量(当它们没有优化掉或进入寄存器时)将指针值本身存储在内存,就像intlong.在 C 中,当您执行 *pty 时,编译器必须将指针值加载到寄存器中, 然后 对指向的内存进行另一次加载。


我假设 DS 指的是指针值本身存储在内存中的数据段。还有那个 sizeof(int)=2,因为对于 16 位 C 实现来说这似乎很可能。

解引用并加载pty指向的内存,即*pty,需要将段指针的段部分加载到段寄存器中,偏移量部分加载到SI中,DI 或 BX(可用作寻址模式一部分的寄存器)。 x86 对此有说明,例如 les / lds.

由于我们可能不想修改 DS,我将只使用 ES。 (不同的汇编程序对段覆盖使用不同的语法,例如 [es: di] 用于 NASM 但我认为可能 es:[di] 用于 TASM。)

;; *ptx = *pty
;; clobbers: ES, DI, and AX
; load *pty
    les  di, [pty]        ; load pty  from [DS:pty] into ES:DI
    mov  ax, es:[di]      ; load *pty into AX

; store *ptx
    les  di, [ptx]        ; load ptx  from [DS:ptx] into ES:DI
    stosw                 ; store to *ptx from AX

STOSW将AX存入ES:DI,DI根据方向标志DF递增或递减。我们不关心 DI 这条指令运行后的值,但是 Turbo C++(和现代 x86 约定)的标准调用约定说 DF=0(向上递增)函数 entry/exit.

如果您还没有了解字符串指令,请使用普通 mov 和另一个段覆盖。

(@MichaelPetch 说 DS 通常在 16 位实模式调用约定中保留调用,但是 ES 可以 在没有 saving/restoring 的情况下被自由破坏,所以显然我猜对了。)


或者如果你可以破坏 DS 和 ES,你可以使用 MOVSW。在这附近使用 push/pop ds 到 save/restore 将是更多的说明。 (但代码量仍然较小)

;; assuming DS is correct for referencing static data like [pty]
    les  di, [pty]        ; load pty  from [DS:pty] into ES:DI
    lds  si, [ptx]        ; load ptx  from [DS:ptx] into DS:SI
    movsw                 ; copy a word from [DS:SI] to [ES:DI]

请注意,我使用了 lds second,因为我假设静态存储中的两个全局变量都可以通过 DS 的传入值访问,而不是任何段值都是另一个远指针的一部分。

如果您有一个 "huge" 或 "large" 内存模型(或其他已知并非所有静态数据都适合一个 64k 段的模型),这会更复杂,但是您的问题没有显示 ptxpty 的实际存储位置。


此外,我假设您不应该根据它们最近的分配方式来优化它们,即使问题向您展示了它们指向的内容。

如果你知道ptx = &x,那么你不需要从内存中加载ptx,你可以只mov [x], ax(再次假设一个代码模型,其中静态数据如x 可通过 DS 访问)。

此外,当 *pty 指向新鲜的 malloced 存储时,读取它毫无意义,因为它尚未初始化。另一种方式是有道理的。我可能分析过度了。