我的编译器任务的一部分包括将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中减去,但为了这个目的,我需要做到这一点。我该如何解决这个问题?
发布于 2022-08-15 01:15:47
您的内存布局是反向的。数组应该始终放在内存中,从低地址到高地址,即使在堆栈上也是如此。所以你应该
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,然后索引到它总是涉及加法,而不是减法。您不能在16位8086上使用AX作为索引寄存器,但是可以使用SI或DI。( ecm指出,还需要WORD PTR告诉汇编程序生成两个字节的存储指令,而不是一个字节。)
(您可能想知道为什么在有效地址中允许“减法”常量位移,而不允许减法索引寄存器。从技术上讲并非如此,指令只能编码16位恒定位移的加法。但是汇编程序会为您处理这个问题,将[BP-6+DI]编码为常量-6的加法。它相当于[BP+(-6)+DI]或[BP+0FFFAh+DI]。)
您可以通过计算DI中的偏移量而不是AX来提高效率,从而避免额外的MOV DI, AX。此外,如果您的编译器能够计算出sizeof(int)是常量2,那么为了提高效率,应该使用SHL而不是MUL进行乘2的运算。所以它可能想看起来更像
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您还可以在另一个寄存器中单独计算整个地址。BX、SI或DI都能工作。请注意,默认情况下,它们会寻址DS段,而BP则会处理SS段。因此,如果您所处的代码模型中的堆栈和数据段可能不同,则需要分段覆盖。
; compute offset in AX as you have done
MOV BX, BP
SUB BX, AX
MOV WORD PTR SS:[BX], 5https://stackoverflow.com/questions/73355894
复制相似问题