assembly 相对于BP的可变偏移(emu8086组件)

yeotifhr  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(137)

我的编译器作业的一部分包括将C程序翻译成8086汇编。假设我有以下C语言:

int a[3];

这个数组声明的翻译汇编代码如下所示(假设默认为0初始化,并且我必须为局部变量使用堆栈分配):

MOV BP, SP
PUSH 0 ; [BP-2] refers to a[0]
PUSH 0 ; [BP-4] refers to a[1] 
PUSH 0 ; [BP-6] refers to a[2]

假设我必须将下面的C代码转换为汇编代码:

a[2]=5;

指数的计算方式如下:

; offset from BP for a[idx] = offset for a[0] + idx * 2

MOV AX, 2 ; AX = idx
MOV BX, 2 ; multiplier
MUL BX    ; DX:AX = idx * 2 (*ignore DX for now*)
MOV BX, 2 ; AX = offset for a[0] = 2
ADD AX, BX; AX = 4 + 2 = 6
MOV [BP-AX], 5

赋值规范保证索引 *2永远不会超过16个字节,因此DX在乘法运算后将始终包含0000H
问题出在最后一行MOV [BP-AX], 5上。AX不能从BP中减去,但为了达到这个目的,我需要这样做。我该如何解决这个问题?

tzcvj98z

tzcvj98z1#

你的内存布局是反向的。数组应该总是从低到高的地址在内存中布局,即使它们在堆栈上。所以你应该

a[0] at bp-6
a[1] at bp-4
a[2] at bp-2

因此,您的代码看起来应该更像:

;; compute the offset 4 in AX as you have already done
MOV DI, AX
MOV WORD PTR [BP-6+DI], 5

所以数组的基址总是BP-6,然后索引到它总是涉及加法,而不是减法。你不能使用AX作为16位8086上的索引寄存器,但是你可以使用SIDI。(正如ecm所指出的,您还需要WORD PTR来告诉汇编程序生成一条双字节存储指令,而不是一条单字节存储指令。)
(You我可能想知道为什么在有效地址中允许常量位移的“减法”,而索引寄存器的减法却不允许。该指令只能对16位常量位移的加法进行编码。但汇编程序会为您处理这一问题,将[BP-6+DI]编码为常量-6的加法。它等效于[BP+(-6)+DI][BP+0FFFAh+DI]。)
首先,您可以通过计算DI中的偏移量而不是AX来提高效率,从而避免额外的MOV DI, AX。此外,如果您的编译器能够计算出sizeof(int)是常量2,那么为了效率起见,乘以2应该用SHL而不是MUL来完成。

MOV DI, 2 ; or some code choosing index 2 at runtime
SHL DI, 1 ; multiply by sizeof(int) which is 2
MOV WORD PTR [BP-6+DI], 5

当然,如果索引2真的是一个常数,那么理想情况下,你会把整个东西优化成MOV WORD PTR [BP-2], 5
更一般地说,如果你不能在一条指令中完成你想要的,就发出更多的指令,在多个步骤中完成它。在某些情况下,你可能需要使用额外的寄存器。
如果你真的想让你的数组倒过来,达到你最初要求的效果,你可以这样做,例如:

; compute offset in AX as you have done
MOV DI, AX
NEG DI
MOV WORD PTR [BP+DI], 5

您也可以在另一个寄存器中单独计算整个地址。BXSIDI都可以。请注意,默认情况下,它们寻址DS段,而BP寻址SS段。因此,如果您使用的代码模型中堆栈和数据段可能不同,则需要使用段覆盖。

; compute offset in AX as you have done
MOV BX, BP
SUB BX, AX
MOV WORD PTR SS:[BX], 5

相关问题