如何将 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));
我的问题是如何在汇编中对以下几点进行编码:
ptx=pty
*ptx=*pty
这些声明应该在全球范围内吗?如果是这样,C 变量的静态存储上将有 asm 标签。如果不是(函数内的局部变量),它们将在堆栈上并且 IDK 他们如何期望您知道它们将位于 BP 的偏移量。
无论哪种方式,它们都是 32 位的 seg:off(小端序,因此在低 16 位中偏移)远指针,因此将一个复制到另一个只是一个 4 字节的副本,您可以使用2 个整数加载 + 存储。
指针变量(当它们没有优化掉或进入寄存器时)将指针值本身存储在内存中,就像int
或long
.在 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 段的模型),这会更复杂,但是您的问题没有显示 ptx
和 pty
的实际存储位置。
此外,我假设您不应该根据它们最近的分配方式来优化它们,即使问题向您展示了它们指向的内容。
如果你知道ptx = &x
,那么你不需要从内存中加载ptx
,你可以只mov [x], ax
(再次假设一个代码模型,其中静态数据如x
可通过 DS 访问)。
此外,当 *pty
指向新鲜的 malloc
ed 存储时,读取它毫无意义,因为它尚未初始化。另一种方式是有道理的。我可能分析过度了。
我要进行汇编测试,我有一个关于汇编指针的问题。我正在尝试做一个练习,但我无法解决它。
考虑 C 中的语句:
int x=100, y=200;
int far *ptx;
int far *pty;
假设指令已经执行:
ptx=&x;
pty=(int *)malloc(sizeof(int));
我的问题是如何在汇编中对以下几点进行编码:
ptx=pty
*ptx=*pty
这些声明应该在全球范围内吗?如果是这样,C 变量的静态存储上将有 asm 标签。如果不是(函数内的局部变量),它们将在堆栈上并且 IDK 他们如何期望您知道它们将位于 BP 的偏移量。
无论哪种方式,它们都是 32 位的 seg:off(小端序,因此在低 16 位中偏移)远指针,因此将一个复制到另一个只是一个 4 字节的副本,您可以使用2 个整数加载 + 存储。
指针变量(当它们没有优化掉或进入寄存器时)将指针值本身存储在内存中,就像int
或long
.在 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 段的模型),这会更复杂,但是您的问题没有显示 ptx
和 pty
的实际存储位置。
此外,我假设您不应该根据它们最近的分配方式来优化它们,即使问题向您展示了它们指向的内容。
如果你知道ptx = &x
,那么你不需要从内存中加载ptx
,你可以只mov [x], ax
(再次假设一个代码模型,其中静态数据如x
可通过 DS 访问)。
此外,当 *pty
指向新鲜的 malloc
ed 存储时,读取它毫无意义,因为它尚未初始化。另一种方式是有道理的。我可能分析过度了。