汇编语言数组求和代码分析(1)

 

 

是这么一段代码,来自于《Intel汇编语言程序设计》(第四版)

 

 

;----------------------------------------------------------------------------

ArraySum PROC

;

;  Calculates the sum of an array of 32-bit integers.

;  Receives : ESI = the  array offset

;                   ECX = number of elements in the array

;  Returns : EAX = sum of the array elements

;----------------------------------------------------------------------------

 

         push esi        ; save ESI,ECX

         push ecx

         mov eax,0     ; set the sum to zero

L1:

         add eax,[esi] ; add each integer to sum

         add esi,4       ; point to next integer

         loop L1          ; repeat for array size

 

         pop ecx         ; restore ECX,ESI

         pop esi

         ret                 ; sum is in EAX

ArraySum ENDP

 

 

以上就是数组就和的函数代码,我们来一句一句的分析。

 

 

首先,我们定义了一个函数,用下面的语句

 

ArraySum PROC

 

然后,我们为了保存esi和ecx的值,将其push进了堆栈中,这样,我们在整个函数结束之后,就可以重新通过pop得到esi和ecx原来的值:

 

         push esi        ; save ESI,ECX

         push ecx

 

然后,因为我们会将我们就得的数组的和一直保存在eax寄存器当中,所以用以下语句将EAX寄存器清零:

 

         mov eax,0     ; set the sum to zero

 

然后,我们增加了一个L1的标号,用于循环,接着我们就开始了循环,看一下代码:

 

L1:

         add eax,[esi] ; add each integer to sum

         add esi,4       ; point to next integer

         loop L1          ; repeat for array size

 

首先,esi寄存器中保存着当前数组的元素的偏移地址,然后通过方括号,可以取得此地址的数组值,将其add到eax中,然后,又将esi这个地址增加4(因为我们这里计算的是32位整数的就和,32位整数,需要4个BYTE,所以,这里我们将esi增加的是4,而不是其他值,这样便得到了下一个数组元素的偏移地址),然后下一句跳回标号L1。

 

说到这里,有人可能会问,那循环如何知道什么时候结束呢?其实在循环的时候,寄存器ECX中保存了循环会执行的次数(这里就是数组元素的个数),看看函数的注释:

 

 

;----------------------------------------------------------------------------

ArraySum PROC

;

;  Calculates the sum of an array of 32-bit integers.

;  Receives : ESI = the  array offset

;                   ECX = number of elements in the array

;  Returns : EAX = sum of the array elements

;----------------------------------------------------------------------------

 

这一句:

 

ECX = number of elements in the array

 

说明了ECX寄存器保留了数组中元素的个数(ECX寄存器保留循环的个数,并且在循环的时候(每执行一次LOOP),会自减1,直至变为0),所以这个其实是需要我们在调用ArraySum 这个函数之前,将数组元素的个数计算出来之后,保留在ECX寄存器中的。

 

到这里,函数的功能就完成了,不过我们还要进行一下收尾工作,看以下的代码:

 

         pop ecx         ; restore ECX,ESI

         pop esi

         ret                 ; sum is in EAX

 

我们需要把ECX和ESI寄存器之前的值赋回去,我们只要弹出ecx和esi就可以了。

 

最后一句ret是所有非主函数都需要带的,用来将 指令指针寄存器ESP 指向函数调用之前的地址,好接着让主函数向下执行。

 

 

然后我们再来看看如何使用这个数组求和函数。

 

.data

array  DWORD 10000h,20000h,30000h,40000h,50000h

theSum DWORD

.code

main PROC

    mov esi , OFFSET array         ; ESI points to array

    mov ecx, LENGTHOF array     ; ECX = array count

    call ArraySum                        ; calculate the sum

    mov theSum , eax                 ; return in EAX

main ENDP

END main

 

在这里,我们看到,主函数确实在执行ArraySum函数之前向esi和ecx寄存器中放入了合适的值^_^。

 

 

另外,我们还可以不使用上面那种容易出错的方法,而使用伪指令 USES 来代替我们进行寄存器值的保存,下面是使用USES修改后的求和代码:

 

ArraySum PROC USES esi ecx

      mov eax , 0

L1:

      add eax , [esi]

      add esi , 4

      loop L1

      ret

ArraySum ENDP

 

编译器将生成以下代码:

 

ArraySum PROC

      push esi

      push ecx

      mov eax , 0

L1:

      add eax , [esi]

      add esi , 4

      loop L1

      pop ecx

      pop esi

      ret

ArraySum ENDP

 

 

 (注意:不能将用于过程的返回值的寄存器进行压栈和弹出,否则其中的值会丢失,例如此例中的EAX)

 

你可能感兴趣的:(工作)