我的编译器作业的一部分包括将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
中减去,但为了达到这个目的,我需要这样做。我该如何解决这个问题?
1条答案
按热度按时间tzcvj98z1#
你的内存布局是反向的。数组应该总是从低到高的地址在内存中布局,即使它们在堆栈上。所以你应该
因此,您的代码看起来应该更像:
所以数组的基址总是
BP-6
,然后索引到它总是涉及加法,而不是减法。你不能使用AX
作为16位8086上的索引寄存器,但是你可以使用SI
或DI
。(正如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
来完成。当然,如果索引2真的是一个常数,那么理想情况下,你会把整个东西优化成
MOV WORD PTR [BP-2], 5
。更一般地说,如果你不能在一条指令中完成你想要的,就发出更多的指令,在多个步骤中完成它。在某些情况下,你可能需要使用额外的寄存器。
如果你真的想让你的数组倒过来,达到你最初要求的效果,你可以这样做,例如:
您也可以在另一个寄存器中单独计算整个地址。
BX
、SI
或DI
都可以。请注意,默认情况下,它们寻址DS
段,而BP
寻址SS
段。因此,如果您使用的代码模型中堆栈和数据段可能不同,则需要使用段覆盖。