在子程序上堆叠

时间:2018-01-08 17:31:45

标签: assembly emu8086

我无法理解堆栈是如何工作的。我很困惑我读过但我的思绪无法得到它。它与bit有关。我不确定我是否正确,但是在堆栈中"职位"是2和2,因为它是2 * 8bits所以它[6]采取第三个元素?我不确定你是否能解释我这部分代码我会很高兴。

SUM_PIN PROC NEAR   ;it starts the routine
V1: MOV BP,SP       ;SP moves to BP.As far as i remember BP is down on stack                            

V2: MOV AX,0        ;AX takes the 0       

V3: MOV BX,[BP+6]          ;it has something to do with 2 bit but i didn't catch it

V4: MOV CX,[BP+4]                         

V5: MOV DL,[BX]

V6: MOV DH,0
    ADD AX,DX

V7: INC BX   ;increase BX 

V8: LOOP V5   ;go back at V5

v9: MOV BX,[BP+2]

v10:MOV [BX],AX

V11:RET   ; return

SUM_PIN ENDP ;end

2 个答案:

答案 0 :(得分:0)

"堆"是普通的计算机内存,使其成为堆栈"按使用方式。在x86-16" 16位实模式下#34;当前"堆栈顶部"是地址ss:sp。与ss=0x1234:sp=0x5678类似,意味着物理内存地址0x179B8被称为"堆栈顶部"。

如果你做call near SUM_PINcall之后的下一条指令的16位偏移量被存储到堆栈中(如push offset <return_address>),即从{{1}中减去2 }} =&gt; sp,并且该内存设置为包含返回地址偏移量(即16位,因此将使用sp=0x56761234:5676个内存单元格。

现在在1234:5677内,SUM_PIN设置为bp,因此它将寻址相同的内存(sp指向该返回地址)。 ss:bp超出此范围,因此无论调用bp+2子例程,它都应该将参数数据放入堆栈内存。

从代码用法中可以调用它,例如:

SUM_PIN

push offset <some_unsigned_byte_array_label> push <length_of_array> push offset <some_label_to_spare_word_memory_to_store_result> call SUM_PIN ; read result of the subroutine from memory into ax mov ax, [some_label_to_spare_word_memory_to_store_result] 内,SUM_PIN将达到上一个ss:bp+2的值(内存地址的偏移量,应该写入结果),push将达到第二次推送存储数组中元素的数量,ss:bp+4将达到第一次推送,这是要求求和的某个数组的地址的偏移量。

确保您了解在哪种情况下使用默认段寄存器,当使用ss:bp+6寻址时,默认段寄存器为[bp+displacement],因此像ss这样的指令将获取内容mov cx,[bp+4]段中的内存,但ss使用mov dl,[bx]作为基址寄存器具有默认段bx,因此它将使用当前{{1}扩展堆栈的偏移量提取} value来计算物理内存地址。

堆栈内存没有什么特别之处,除了你有许多隐含操作的指令(ds),而在16位实模式中,&#34; OS&#34; (中断)与用户app共享堆栈,所以ds以下的值被定时器/键盘/中断每秒覆盖几次...中断存储返回地址和保留的寄存器值,即你不能使用存储器可靠地存储您的值。

覆盖/ push, pop, call, ret, enter, leave, pushf, popf, ...以上的值将覆盖当前堆栈中的值,因此这不是存储值的完美位置。但是如果你第一次&#34;保留&#34;通过执行ss:sp之类的操作,您可以安全地使用该空间ss:sp(现在中断将其值存储在当前sub sp,40之下),然后&#34;发布&#34;恢复sp+0 .. sp+39,例如sp

总的来说,您应该使用一些调试器(emu8086有一个),在sp区域打开内存视图,并使用add sp,40指令查看内存中的值是如何演变的,以及{{1}通过其他指令在该区域上/下移动。

答案 1 :(得分:-1)

自从我组装以来已经过了很长时间,这是x86的味道......但是我会尝试引导你完成这个(通过一些猜测)

SUM_PIN PROC NEAR   ;it starts the routine
V1: MOV BP,SP       ;SP moves to BP.As far as i remember BP is down on stack                            

这会加载BP寄存器中的堆栈指针值

V2: MOV AX,0        ;AX takes the 0       

AX = 0

V3: MOV BX,[BP+6]          ;it has something to do with 2 bit but i didn't catch it

加载BX的值比BP指向的值高6个字节。由于自MOV BP,SP&#39以来BP未被修改,因此与当前堆栈位置之上的六个字节相同&#34;

V4: MOV CX,[BP+4]                         

使用高于堆栈底部四个字节的值加载CX。 &#34; LOOP&#34;命令将自动递减该值,并且将通过&#34;当CX达到0时

V5: MOV DL,[BX]

使用BX指向的地址中的值加载DL

V6: MOV DH,0
    ADD AX,DX

AX = AX + DL

V7: INC BX   ;increase BX 

向BX添加一个(移动到BX指向的下一个字节)

V8: LOOP V5   ;go back at V5


v9: MOV BX,[BP+2]

使用最后一个参数的值将BX加载到函数中(BP == SP ==堆栈底部==地址,以便在调用RET时返回)

v10:MOV [BX],AX

将AX存储在BX指向的地址

V11:RET   ; return
SUM_PIN ENDP ;end

理解这一点的关键是堆栈增长 DOWNWARD 。因此,如果将两个字节的值压入堆栈,SP = SP - 2

知道并阅读此代码,我可以看出[BP + 4]是一个2字节的值。我猜测[BP + 6]是一个指向字节数组的指针。

这个的C / C ++签名可以读作(类似):

void SUM_PIN(byte* first, int someValueThatIsNotUsed, int *total)

所以我猜C / C ++版本会是这样的:

void SUM_PIN(byte* first, int count, int *total) {
        int A = 0;
        byte *b = first;

    while (count-- > 0) {
            a = a + *b
            b = b + 1
        }

        return a
    }

编辑:基于评论,CX自动被评估为AL&#34; flavor&#34;的特征。我忘记了或者没有意识到。我修改了我的for循环来反映