汇编程序的多模块化

来源:互联网 发布:mac os 分辨率设置 编辑:程序博客网 时间:2024/05/21 11:22
      在此章节之前,创建的程序全部是只用一个模块的,也就是程序中只用了一个END指令,这里通过创建ArraySum例子程序演示创建多模块程序的方法。


首先通过使用ENTERN伪指令创建传统的多模块程序,这里的ENTERN伪指令实际上只起了调用其他模块里的过程前对此过程的声明作用。这里的被调用过程与一个模块表达式没有书写格式上的变化,源程序如下:

(_prompt.asm模块)

[cpp] view plaincopyprint?
  1. TITLE Prompt For Integers         (_prompt.asm)  
  2.   
  3. INCLUDE Irvine32.inc  
  4. .code  
  5. ;----------------------------------------------------  
  6. PromptForIntegers PROC  
  7.   
  8. ; 提示用户输入三个整数,调用ReadInt读取输入返回到eax中,  
  9. ; 然后把值插入到数组中.  
  10. ; 参数:  
  11. ;   ptrPrompt:PTR BYTE          ; 提示用户的字符串  
  12. ;   ptrArray:PTR DWORD          ; 数组首地址  
  13. ;   arraySize:DWORD         ; 数组的长度  
  14. ; 返回值:无  
  15. ;-----------------------------------------------------  
  16. arraySize   EQU [ebp+16]  
  17. ptrArray    EQU [ebp+12]  
  18. ptrPrompt   EQU [ebp+8]         ; [ebp+8]指向调用此模块(call指令)之前的入栈数据  
  19.   
  20.     enter   0,0           
  21. ; enter指令自动为被调用过程创建堆栈框架(此处相当于push ebp;mov ebp,esp;sub esp,0),  
  22. ; 并为局部变量创建堆栈空间(enter 0,0  前一个0表示局部变量占用的字节数,  
  23. ; 后一个0表示从调用过程复制到当前堆栈框架中的堆栈框架指针的数目)  
  24.     pushad              ; 8个通用寄存器入栈  
  25.   
  26.     mov ecx,arraySize       ; ecx表示数组的大小,这里同时是循环计数器  
  27.     cmp ecx,0           ; 循环计数器 <= 0?  
  28.     jle L2              ; 是:跳过循环体  
  29.     mov edx,ptrPrompt       ; 否:把提示字符串首地址传递给edx  
  30.     mov esi,ptrArray        ; esi指向数组的首地址  
  31.   
  32. L1: call    WriteString     ; 预先定义的过程,输出edx代表的字符串  
  33.     call    ReadInt         ; 等待用户输入后,从标准输入中读取一个32位有符号整数并在eax中返回  
  34.     call    Crlf              
  35.     mov [esi],eax           ; 把输入的值存储在数组中  
  36.     add esi,4           ; esi指向数组下一个元素  
  37.     loop    L1  
  38.   
  39. L2: popad               ; 8个通用寄存器出栈  
  40.     leave  
  41. ; leave指令释放一个过程的堆栈框架,执行与enter相反的动作(此处相当于mov esp,ebp;pop ebp)  
  42. ; 如果enter指令创建了局部变量,leave还要释放局部变量占用的堆栈空间  
  43.     ret 12                
  44. ; 调用此模块前,主模块会把此模块用到的相应参数入栈,这里12表示此模块结束后释放主模块中占用的堆栈空间  
  45. ; 相当于依次执行ret;add esp,12的指令  
  46. PromptForIntegers ENDP  
  47. END  

(_arrysum.asm模块):

[cpp] view plaincopyprint?
  1. TITLE ArraySum Procedure             (_arrysum.asm)  
  2.   
  3. INCLUDE Irvine32.inc  
  4. .code  
  5. ArraySum PROC  
  6. ;  
  7. ; 计算有符号32位整数数组的各元素总和  
  8. ; 参数:  
  9. ;   ptrArray        ; 指向数组首地址  
  10. ;   arraySize       ; 数组的长度(DWORD)  
  11. ; 返回值:  EAX = sum  
  12. ;-----------------------------------------------------  
  13. ptrArray EQU [ebp+8]  
  14. arraySize EQU [ebp+12]  
  15.   
  16.     enter   0,0     ; enter指令创建堆栈框架,此处无局部变量  
  17.     push    ecx         ; 提示:eax作为返回值,不能入栈  
  18.     push    esi  
  19.   
  20.     mov eax,0       ; 初始化数组总和为0  
  21.     mov esi,ptrArray    ; 数组首地址  
  22.     mov ecx,arraySize   ; 数组大小,同时是循环计数器  
  23.     cmp ecx,0       ; 循环计数器 <= 0?  
  24.     jle L2          ; 是: 跳过循环体  
  25.   
  26. L1: add eax,[esi]       ; 否:把数组一个元素加到eax中  
  27.     add esi,4       ; 指向下一个数组元素  
  28.     loop    L1  
  29.   
  30. L2: pop esi  
  31.     pop ecx         ; 总和通过eax返回到调用过程(主模块)  
  32.     leave           ; 释放enter创建的堆栈框架  
  33.     ret 8           ; 释放调用过程中(主模块)的参数通过堆栈传递时占用的堆栈空间  
  34. ArraySum ENDP  
  35. END  

(_display.asm模块):

[cpp] view plaincopyprint?
  1. TITLE DisplaySum Procedure      (_display.asm)  
  2.   
  3. INCLUDE Irvine32.inc  
  4. .code  
  5. ;-----------------------------------------------------  
  6. DisplaySum PROC  
  7.   
  8. ; 在控制台窗口中显示总和的值  
  9. ; 参数:  
  10. ;   ptrPrompt       ; 提示字符串首地址  
  11. ;   theSum      ; 数组各元素总和(DWORD)  
  12. ; 返回值: 无  
  13. ;-----------------------------------------------------  
  14.   
  15. theSum  EQU [ebp+12]  
  16. ptrPrompt   EQU [ebp+8]  
  17.   
  18.     enter   0,0     ; enter指令创建堆栈框架,此处无局部变量  
  19.     push    eax  
  20.     push    edx  
  21.   
  22.     mov edx,ptrPrompt   ; 预先定义的过程参数  
  23.     call    WriteString ; 输出提示字符串到屏幕  
  24.     mov eax,theSum  ; 预先定义的过程参数  
  25.     call    WriteInt        ; 输出数组总和到屏幕  
  26.     call    Crlf  
  27.   
  28.     pop edx  
  29.     pop eax  
  30.     leave  
  31.     ret 8           ; 返回到主模块并释放主模块调用此过程时占用的堆栈空间  
  32. DisplaySum ENDP  
  33. END  


(Sum_main.asm主模块):

[cpp] view plaincopyprint?
  1. TITLE Integer Summation Program      (Sum_main.asm)  
  2.   
  3. ; 多模块程序例子:   (主模块/启动模块)  
  4. ; 这个程序提示用户输入3个32位有符号整数,  
  5. ; 然后把输入的整数存储到数组中并计算这个数组的总和,  
  6. ; 显示数组的总和到屏幕  
  7.   
  8. INCLUDE Irvine32.inc    ;INCLUDE 伪指令包含用户定义的.inc文件,其中可集中放置变量或过程的声明等信息  
  9.   
  10. EXTERN PromptForIntegers@0:PROC         ; EXTERN 伪指令在调用外部模块中的过程前声明,@0表示PromptForIntegers过程没有PROC伪指令参数  
  11. EXTERN ArraySum@0:PROC, DisplaySum@0:PROC   ; EXTERN 伪指令在调用外部模块中的过程前声明,其余同上  
  12.   
  13. ; 为被调用过程赋予其他的标号,方便主模块调用  
  14. ArraySum            EQU ArraySum@0  
  15. PromptForIntegers   EQU PromptForIntegers@0  
  16. DisplaySum      EQU DisplaySum@0  
  17.   
  18. Count = 3                                   ; 标号代表定义数组的大小  
  19.   
  20. .data  
  21. prompt1 BYTE  "Enter a signed integer: ",0  
  22. prompt2 BYTE  "The sum of the integers is: ",0  
  23. array   DWORD  Count DUP(?)                 ; 定义包含count个元素的未初始化数组  
  24. sum     DWORD  ?  
  25.   
  26. .code  
  27. main PROC  
  28.     call    Clrscr                          ; 清理屏幕  
  29.   
  30. ; 调用PromptForIntegers( addr prompt1, addr array, Count ),括号中为向被调用过程包含的参数  
  31.     push    Count  
  32.     push    OFFSET array  
  33.     push    OFFSET prompt1  
  34.     call    PromptForIntegers  
  35.   
  36. ; 调用eax = ArraySum( addr array, Count ),其中eax是被调用过程的返回值  
  37.     push    Count  
  38.     push    OFFSET array  
  39.     call    ArraySum  
  40.     mov sum,eax  
  41.   
  42. ; 调用DisplaySum( addr prompt2, sum )  
  43.     push    sum  
  44.     push    OFFSET prompt2  
  45.     call    DisplaySum  
  46.   
  47.     call    Crlf  
  48.     exit  
  49. main ENDP  
  50.   
  51. END main  

下面是这个程序运行的结果:
0 0