1. 堆棧框架
1). 棧參數(shù)
之前使用寄存器傳遞參數(shù)氧敢,現(xiàn)在我們將使用運(yùn)行棧在子程序中傳遞參數(shù)。堆椦牛框架(或激活記錄)是為傳遞的參數(shù)福稳,子程序返回地址,局部變量和已保存的寄存器預(yù)留的堆棧區(qū)域瑞侮。棧框架的創(chuàng)建步驟:
I. 如果有要傳遞的參數(shù)鼓拧,則壓入棧中半火。
II. 調(diào)用子程序,使子程序返回地址被壓入堆棧季俩。
III. 子程序開(kāi)始執(zhí)行時(shí)钮糖,EBP被壓入棧中。
IV. EBP等于ESP酌住。 從這一點(diǎn)開(kāi)始店归,EBP充當(dāng)所有子例程參數(shù)的基礎(chǔ)參考。
V. 如果存在局部變量酪我,則ESP遞減以為堆棧上的變量保留空間消痛。
VI. 如果需要保存任何寄存器,則將它們壓入堆棧都哭。
2). 寄存器參數(shù)的缺點(diǎn)
缺點(diǎn):相同的寄存器用于保存數(shù)據(jù)值秩伞,例如計(jì)算中的循環(huán)計(jì)數(shù)器和操作數(shù)逞带。 因此,在程序之前必須首先將用作參數(shù)的任何寄存器壓入堆棧
調(diào)用纱新,分配過(guò)程參數(shù)的值展氓,然后在過(guò)程返回后恢復(fù)為原始值。例如:
push ebx ; save register values
push ecx
push esi
mov esi,OFFSET array ; starting OFFSET
mov ecx,LENGTHOF array ; size, in units
mov ebx,TYPE array ; doubleword format
call DumpMem ; display memory
pop esi ; restore register values
pop ecx
pop ebx
不僅所有額外的推送和彈出都會(huì)造成代碼混亂脸爱,它們往往會(huì)通過(guò)避免寄存器參數(shù)來(lái)消除我們希望獲得的性能優(yōu)勢(shì)遇汞! 此外,程序員必須非常小心每個(gè)寄存器的PUSH與其適當(dāng)?shù)腜OP匹配簿废,即使代碼中存在多個(gè)執(zhí)行路徑空入。
兩種參數(shù)類型被壓入棧中產(chǎn)生的子程序調(diào)用方式有:值傳遞、引用傳遞捏鱼。
- 值傳遞
向棧中存入變量的值
.data
val1 DWORD 5
val2 DWORD 6
.code
push val2
push val1
call AddTwo
- 引用傳遞
向棧中壓入變量的地址
push OFFSET val2
push OFFSET val1
call Swap
- 傳遞數(shù)組
高級(jí)語(yǔ)言通過(guò)引用傳遞向子程序傳遞數(shù)組执庐。
.data
array DWORD 50 DUP(?)
.code
push OFFSET array
call ArrayFill
3). 接收棧參數(shù)
- 基址偏移尋址
我們使用基址偏移地址接收棧參數(shù),EBP是一個(gè)基礎(chǔ)的寄存器并且偏移量是一個(gè)常量导梆。
AddTwo PROC
push ebp
mov ebp,esp ; base of stack frame
mov eax,[ebp + 12] ; second parameter
add eax,[ebp + 8] ; first parameter
pop ebp
ret
AddTwo ENDP
- 顯示堆棧參數(shù)
顯示堆棧參數(shù):棧參數(shù)使用表達(dá)式引用轨淌。
y_param EQU [ebp + 12]
x_param EQU [ebp + 8]
AddTwo PROC
push ebp
mov ebp,esp
mov eax,y_param
add eax,x_param
pop ebp
ret
AddTwo ENDP
- 清理堆棧
當(dāng)子程序返回后,我們必須從堆棧中移除參數(shù)看尼。否則递鹉,會(huì)早晨?jī)?nèi)存泄漏和棧損壞。
4). 32位調(diào)用約定
- C調(diào)用約定
C調(diào)用約定通常在C和C++程序中使用藏斩,子程序參數(shù)以相反的順序被壓入堆棧躏结,因此進(jìn)行函數(shù)調(diào)用的C程序?qū)⑹紫葘推入堆棧,然后按A狰域。C調(diào)用約定解決清理堆棧的方法是:當(dāng)程序調(diào)用子程序時(shí)媳拴,它在CALL指令后面跟一個(gè)語(yǔ)句,該語(yǔ)句向堆棧指針(ESP)添加一個(gè)等于子程序參數(shù)組合大小的值兆览。
Example1 PROC
push 6
push 5
call AddTwo
add esp,8 ; remove arguments from the stack
ret
Example1 ENDP
- STDCALL調(diào)用約定
當(dāng)程序使用RET時(shí)屈溉,在RET后跟一個(gè)整型參數(shù),整型參數(shù)的值為該子程序參數(shù)的大小抬探。
AddTwo PROC
push ebp
mov ebp,esp ; base of stack frame
mov eax,[ebp + 12] ; second parameter
add eax,[ebp + 8] ; first parameter
pop ebp
ret 8 ; clean up the stack
AddTwo ENDP
- 保存和恢復(fù)寄存器
子程序經(jīng)常在修改寄存器前保存他們當(dāng)前的內(nèi)容子巾。這是一個(gè)好方法,因?yàn)樵瓟?shù)據(jù)在子程序返回之后可以被恢復(fù)小压。
MySub PROC
push ebp ; save base pointer
mov ebp,esp ; base of stack frame
push ecx
push edx ; save EDX
mov eax,[ebp+8] ; get the stack parameter
.
.
pop edx ; restore saved registers
pop ecx
pop ebp ; restore base pointer
ret ; clean up the stack
MySub ENDP
5). 局部變量
局部變量在運(yùn)行棧上創(chuàng)建线梗,通常在基址(EBP)之下。
MySub PROC
push ebp
mov ebp,esp
sub esp,8 ; create locals
mov DWORD PTR [ebp?4],10 ; X
mov DWORD PTR [ebp?8],20 ; Y
mov esp,ebp ; remove locals from stack
pop ebp
ret
MySub ENDP
- 局部變量符號(hào)
為了閱讀更簡(jiǎn)便怠益,可以定義符號(hào)為每個(gè)局部變量偏移地址仪搔。
X_local EQU DWORD PTR [ebp?4]
Y_local EQU DWORD PTR [ebp?8]
MySub PROC
push ebp
mov ebp,esp
sub esp,8 ; reserve space for locals
mov X_local,10 ; X
mov Y_local,20 ; Y
mov esp,ebp ; remove locals from stack
pop ebp
ret
MySub ENDP
6). 引用參數(shù)
引用參數(shù)通常使用基址偏移地址(EBP)被程序所訪問(wèn)。因?yàn)槊總€(gè)引用參數(shù)是一個(gè)指針溉痢,它通常被加載到寄存器中以用作間接操作數(shù)僻造。
- 數(shù)組填充實(shí)例
; 數(shù)組填充
INCLUDE Irvine32.inc
.data
count = 100
array WORD count DUP(?)
.code
main PROC
push OFFSET array
push count
call ArrayFill
call Crlf
call WaitMsg
exit
main ENDP
ArrayFill PROC
push ebp
mov ebp, esp
pushad ; 保存寄存器
mov esi, [ebp + 12] ; 數(shù)組地址
mov ecx, [ebp + 8] ; 數(shù)組長(zhǎng)度
cmp ecx, 0 ; ECX == 0?
je L2 ; 如果是憋他,則結(jié)束循環(huán)
L1:
mov eax, 10000 ; 從0——10000獲取隨機(jī)數(shù)
call RandomRange ; 從Irvine32庫(kù)中
mov [esi], ax ; 插入數(shù)組
add esi, TYPE WORD ; 移動(dòng)到下一個(gè)結(jié)點(diǎn)
LOOP L1
L2:
popad
pop ebp
RET 8 ; 清除堆棧
ArrayFill ENDP
END
7). LEA 指令
LEA指令返回間接操作數(shù)的地址。因?yàn)殚g接操作數(shù)包含一個(gè)或多個(gè)寄存器髓削,它們的偏移量在運(yùn)行時(shí)計(jì)算竹挡。
makeArray PROC
push ebp
mov ebp,esp
sub esp,32 ; myString is at EBP?30
lea esi,[ebp–30] ; load address of myString
mov ecx,30 ; loop counter
L1: mov BYTE PTR [esi],'*' ; fill one position
inc esi ; move to next
loop L1 ; continue until ECX = 0
add esp,32 ; remove the array (restore ESP)
pop ebp
ret
makeArray ENDP
8). ENTER 和 LEAVE 指令
- ENTER 指令
ENTER 指令調(diào)用程序時(shí)自動(dòng)創(chuàng)建一個(gè)堆棧框架立膛。它為局部變量保留堆椌竞保空間并將EBP保存在堆棧中。具體來(lái)說(shuō)它執(zhí)行三個(gè)操作:
I. 將EBP壓入棧中(push ebp)
II. 設(shè)置EBP為堆棻Ρ茫框架的基地址(mov ebp, esp)
III. 為局部變量預(yù)留空間(sub esp, numbytes)
格式
ENTER numbytes, nestinglevel
第一個(gè)參數(shù)是一個(gè)長(zhǎng)廊好啰,為局部變量預(yù)留的字節(jié)空間。第二個(gè)參數(shù)指定過(guò)程的詞法嵌套級(jí)別儿奶。這兩個(gè)操作數(shù)都是立即數(shù)框往。numbytes是4的倍數(shù)。在我們的程序中闯捎,nestinglevel總是0.
示例:
MySub PROC
enter 8,0
; 類似于
MySub PROC
push ebp
mov ebp,esp
sub esp,8
- LEAVE 指令
LEAVE指令終止進(jìn)程的堆棧幀椰弊。 它通過(guò)將ESP和EBP恢復(fù)為調(diào)用過(guò)程時(shí)分配的值來(lái)反轉(zhuǎn)前一個(gè)ENTER指令的操作。
格式
MySub PROC
enter 8,0
.
.
leave
ret
MySub ENDP
9). LOCAL 指令
LOCAL 定義一個(gè)或多個(gè)局部變量名并分配它們的類型瓤鼻。
格式
LOCAL varlist
; 其中每個(gè)變量類似于label:type
示例:
MySub PROC
LOCAL TempArray[10]:DWORD
BubbleSort PROC
LOCAL temp:DWORD, SwapFlag:BYTE
2. 遞歸
遞歸子程序是指直接或間接的調(diào)用它自身秉版。在處理具有重復(fù)模式的數(shù)據(jù)結(jié)構(gòu)時(shí),遞歸(調(diào)用遞歸子例程的實(shí)踐)可以是一個(gè)強(qiáng)大的工具茬祷。
- 無(wú)窮的遞歸
; 無(wú)窮的遞歸
INCLUDE Irvine32.inc
.data
endlessStr BYTE "This recursion never stops",0
.code
main PROC
call Endless
call Crlf
call WaitMsg
exit
main ENDP
Endless PROC
mov edx,OFFSET endlessStr
call WriteString
call Crlf
call Endless
ret ; never executes
Endless ENDP
END
1). 遞歸計(jì)算和
有用的遞歸子例程始終包含終止條件清焕。 當(dāng)終止條件變?yōu)檎鏁r(shí),當(dāng)程序執(zhí)行所有掛起的RET指令時(shí)祭犯,堆棧將展開(kāi)秸妥。
; 遞歸計(jì)算和
INCLUDE Irvine32.inc
.code
main PROC
mov ecx, 5 ; 設(shè)置count = 5
mov eax, 0 ; 設(shè)置sum = 0
call CalcSum ; 計(jì)算和
L1:
call WriteDec ; 顯示EAX
call Crlf
call WaitMsg
exit
main ENDP
;----------------------------------------------
; 計(jì)算一串?dāng)?shù)字之和
; Receives: ECX = count
; Returns: EAX = sum
;----------------------------------------------
CalcSum PROC
cmp ecx, 0 ; 檢查counter
jz L2 ; 如果為0, 則退出程序
add eax, ecx ; 否則求和
dec ecx ; counter遞減
call CalcSum ; 遞歸
L2:
ret
CalcSum ENDP
END
2). 計(jì)算因子
; 遞歸相乘
INCLUDE Irvine32.inc
.code
main PROC
push 5 ; 計(jì)算5!
call Factorial ; 計(jì)算因子
call WriteDec ; 顯示EAX
call Crlf
call WaitMsg
exit
main ENDP
;----------------------------------------------
; 計(jì)算因子乘積
; Receives: [ebp + 8] = n, 計(jì)算
; Returns: EAX = 乘積
;----------------------------------------------
Factorial PROC
push ebp
mov ebp, esp
mov eax, [ebp + 8] ; 獲取n
cmp eax, 0 ; 判斷 n > 0?
ja L1 ; 繼續(xù)L1
mov eax, 1 ; 如果不是沃粗,則返回1
jmp L2 ; 返回結(jié)果
L1:
dec eax
push eax
call Factorial
ReturnFact:
mov ebx, [ebp + 8] ; 獲取n
mul ebx ; EDX:EAX = EAX * EBX
L2:
pop ebp
ret 4
Factorial ENDP
END
3. INVOKE, ADDR, PROC, and PROTO
在32位模式下筛峭,INVOKE,PROC和PROTO指令為定義和調(diào)用過(guò)程提供了強(qiáng)大的工具陪每。除了這些指令外,ADDR運(yùn)算符也是必不可少的工具
定義程序參數(shù)镰吵。
1). INVOKE 指令
INVOKE指令檩禾,僅在32位模式下可用,將參數(shù)壓入棧中和調(diào)用程序疤祭。INVOKE是CALL指令的一個(gè)方便替代品盼产,因?yàn)樗试S您使用一行代碼傳遞多個(gè)參數(shù)。
- 格式
INVOKE procedureName [, argumentList]
EAX,EDX重寫: 如果將小于32位的參數(shù)傳遞給程序勺馆,INVOKE會(huì)經(jīng)常重寫EAX和EDX戏售,因此在使用INVOKE調(diào)用程序時(shí)侨核,最好使用保存和恢復(fù)EAX、DEX的方式灌灾。
- 替換代碼
push TYPE array
push LENGTHOF array
push OFFSET array
call DumpArray
; call 四行代碼等價(jià)于 INVOKE 一行代碼
INVOKE DumpArray, OFFSET array, LENGTHOF array, TYPE array
2). ADDR 操作
ADDR 操作搓译,32位模式下可用,使用INVOKE指令時(shí)可以傳遞指針參數(shù)锋喜。
- 格式
INVOKE FillArray, ADDR myArray
3). PROC 指令
- PROC 指令語(yǔ)法
label PROC [attributes] [USES reglist], parameter_list
其中l(wèi)abel是用戶自定義標(biāo)簽些己,attributes可選值為[distance] [langtype] [visibility] [prologuearg]
- 參數(shù)列表
label PROC [attributes] [USES reglist],
parameter_1,
parameter_2,
.
.
parameter_n
; 其中每個(gè)parameter_n的格式為paramName:type
- 由PROC修改的RET指令
push ebp
mov ebp,esp
.
.
leave
ret (n*4)
- 指定參數(shù)傳遞協(xié)議
Example1 PROC C,
parm1:DWORD, parm2:DWORD
Example1 PROC STDCALL,
parm1:DWORD, parm2:DWORD
4). PROTO 指令
在64位中,我們可以使用PROTO指令標(biāo)識(shí)外部的程序嘿般。
ExitProcess PROTO
.code
mov ecx,0
call ExitProcess
在32位中段标,PROTO是一個(gè)更強(qiáng)大的功能,因?yàn)樗梢园粋€(gè)列表
程序參數(shù)炉奴。
; 類型于C語(yǔ)言中的函數(shù)聲明
MySub PROTO ; procedure prototype
.
INVOKE MySub ; procedure call
.
MySub PROC ; procedure implementation
.
.
MySub ENDP
- 程序聲明的方法
I> 將PROC改為PROTO
II> 移除USES操作和寄存器列表
; 程序 PROTO 聲明:
ArraySum PROTO,
ptrArray:PTR DWORD, ; points to the array
szArray:DWORD ; array size
; 程序?qū)崿F(xiàn)
ArraySum PROC USES esi ecx,
ptrArray:PTR DWORD, ; points to the array
szArray:DWORD ; array size
; (remaining lines omitted...)
ArraySum ENDP
編譯時(shí)參數(shù)檢查
PROTO 指令編譯時(shí)比較程序聲明和程序?qū)崿F(xiàn)的參數(shù)逼庞。MASM 檢測(cè)錯(cuò)誤
如果參數(shù)超出聲明參數(shù)的大小,MASM將生成一個(gè)錯(cuò)誤瞻赶。MASM 不檢測(cè)的錯(cuò)誤
如果參數(shù)小于聲明參數(shù)的大小赛糟,MASM將不生成錯(cuò)誤。數(shù)組求和示例
; 數(shù)組求和
INCLUDE Irvine32.inc
.data
array DWORD 10000h, 20000h, 30000h, 40000h, 50000h
theSum DWORD ?
.code
ArraySum PROTO,
ptrArray: PTR DWORD, ; points to the array
szArray:DWORD ; array size
main PROC
INVOKE ArraySum,
ADDR array, ; 數(shù)組首地址
LENGTHOF array ; 數(shù)組元素個(gè)數(shù)
call WriteHex
call Crlf
call WaitMsg
exit
main ENDP
ArraySum PROC USES esi ecx,
ptrArray: PTR DWORD, ; points to the array
szArray:DWORD ; array size
mov esi, ptrArray ; 數(shù)組地址
mov ecx, szArray ; 數(shù)組長(zhǎng)度
mov eax, 0 ; 設(shè)置和為0
cmp ecx, 0 ; 數(shù)組長(zhǎng)度是否為0
je L2 ; 如果等于則quit
L1:
add eax, [esi] ; 求和
add esi, 4 ; 移動(dòng)到下一個(gè)數(shù)組元素
LOOP L1 ; 循環(huán)
L2:
ret ; sum 在EAX寄存器中
ArraySum ENDP
END
5). 參數(shù)分類
過(guò)程參數(shù)通常根據(jù)調(diào)用程序和被調(diào)用過(guò)程之間的數(shù)據(jù)傳輸方向進(jìn)行分類:
Input -> 輸入?yún)?shù)是由調(diào)用程序傳遞給過(guò)程的數(shù)據(jù).
Output -> 當(dāng)調(diào)用程序?qū)⒆兞康牡刂穫鬟f給過(guò)程時(shí)共耍,將創(chuàng)建輸出參數(shù).
Input-Output -> 輸入輸出參數(shù)與輸出參數(shù)相同虑灰,但有一個(gè)例外:被調(diào)用過(guò)程期望參數(shù)引用的變量包含一些數(shù)據(jù)。
6). 示例:交換兩個(gè)整數(shù)
; 交換兩個(gè)數(shù)
INCLUDE Irvine32.inc
Swap PROTO, pValX: PTR DWORD, pValY: PTR DWORD
.data
Array DWORD 10000h, 20000h
.code
main PROC
; 交換之前顯示數(shù)組
mov esi, OFFSET Array
mov ecx, 2 ; 數(shù)組長(zhǎng)度
mov ebx, TYPE Array
call DumpMem ; 顯示數(shù)組數(shù)據(jù)
INVOKE Swap, ADDR Array, ADDR[Array + 4]
; 顯示交換后的數(shù)據(jù)
call DumpMem
call Crlf
call WaitMsg
exit
main ENDP
;--------------------------------------------
; 交換兩個(gè)32位整型
; Return: 無(wú)
;--------------------------------------------
Swap PROC USES eax esi edi,
pValX: PTR DWORD,
pValY: PTR DWORD
mov esi, pValX ; 獲取指針
mov edi, pValY
mov eax, [esi] ; 獲取第一個(gè)整數(shù)
xchg eax, [edi] ; 交換兩個(gè)數(shù)
mov [esi], eax ; 替換第一個(gè)整數(shù)
RET
Swap ENDP
END
7). 調(diào)試提醒
常見(jiàn)錯(cuò)誤:
- 參數(shù)大小不匹配
- 傳遞指針類型錯(cuò)誤
- 傳遞即時(shí)值
8). WriteStackFrame 程序
; 交換兩個(gè)數(shù)
INCLUDE Irvine32.inc
WriteStackFrame PROTO,
numParam:DWORD, ; 傳遞參數(shù)個(gè)數(shù)
numLocalVal: DWORD, ; 本地變量
numSavedReg: DWORD ; 保存寄存器的個(gè)數(shù)
.code
myProc PROC USES eax ebx,
x: DWORD, y: DWORD
LOCAL a: DWORD, b: DWORD
PARAMS = 2
LOCALS = 2
SAVED_REGS = 2
mov a, 0AAAAh
mov b, 0BBBBh
INVOKE WriteStackFrame, PARAMS, LOCALS, SAVED_REGS
RET
myProc ENDP
main PROC
mov eax, 0EAEAEAEAh
mov ebx, 0EBEBEBEBh
INVOKE myProc, 1111h, 2222h;
call Crlf
call WaitMsg
exit
main ENDP
END
4. 創(chuàng)建多模塊程序
一個(gè)很大的源文件管理起來(lái)困難并且編譯起來(lái)比較慢痹兜,可以將單個(gè)文件拆分成多個(gè)文件穆咐,但是對(duì)任何源文件的修改仍然需要完整組裝所有文件。更好的方法是將程序劃分為模塊(組合單元)字旭。每個(gè)模塊都是獨(dú)立組裝的对湃,因此更改一個(gè)模塊的源代碼只需要重新組裝單個(gè)模塊。 鏈接器將所有已組裝的模塊(OBJ文件)組合成一個(gè)可執(zhí)行文件很快遗淳。 鏈接大量對(duì)象模塊所需的時(shí)間遠(yuǎn)遠(yuǎn)少于組裝相同數(shù)量的源代碼文件所需的時(shí)間.
有兩種方法去創(chuàng)建多模塊程序:第一個(gè)是傳統(tǒng)的拍柒,使用EXTERN
指令,這或多或少可以在不同的x86匯編程序中移植屈暗。第二種是使用微軟的高級(jí)指令INVOKE
和PROTO
拆讯,隱藏更多的低級(jí)細(xì)節(jié)。
1). 隱藏和導(dǎo)出程序名稱
默認(rèn)情況下养叛,MASM讓所有的程序時(shí)public
种呐,它們可以被任意模塊被調(diào)用。我們可以覆蓋這個(gè)行為弃甥,使用PRIVATE
修飾符爽室,mySub PROC PRIVATE
。
-
OPTION PROC:PRIVATE指令
:源程序模塊之外的程序總是隱藏淆攻。所有的程序默認(rèn)的訪問(wèn)修飾符變?yōu)樗接械睦铡H缓笪覀儽仨毷褂?code>PUBLIC指令標(biāo)識(shí)它們嘿架。
OPTION PROC:PRIVATE
PUBLIC mySub
2). 調(diào)用外部程序
EXTERN
指令,在調(diào)用當(dāng)前模塊外部的過(guò)程時(shí)使用啸箫,標(biāo)識(shí)過(guò)程的名稱和堆棧幀大小耸彪。
INCLUDE Irvine32.inc
EXTERN sub1@0:PROC
.code
main PROC
call sub1@0
exit
main ENDP
END main
3). 跨模塊邊界使用變量和符號(hào)
- 導(dǎo)出變量和符號(hào)
在一個(gè)閉合的模塊中,變量和符號(hào)默認(rèn)是私有的筐高。我們可以使用PUBLIC
指令導(dǎo)出特殊的名字搜囱。
PUBLIC count, SYM1
SYM1 = 10
.data
count DWORD 0
- 接收外部變量 和符號(hào)
我們可以使用EXTERN
指令接收定義在其他模塊的變量和符號(hào)
EXTERN name : type
- 使用
INCLUDE
和EXTERNDEF
vars.inc
; vars.inc
EXTERNDEF count:DWORD, SYM1:ABS
sub1.asm
; sub1.asm
.386
.model flat,STDCALL
INCLUDE vars.inc
SYM1 = 10
.data
count DWORD 0
END
main.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess proto, dwExitCode:dword
INCLUDE vars.inc
.code
main PROC
mov count,2000h
mov eax,SYM1
INVOKE ExitProcess,0
main ENDP
END main
4). 示例:數(shù)組求和
模塊圖結(jié)構(gòu):
5). 使用EXTERN
創(chuàng)建模塊
_prompt.asm
; 提示用戶輸入整數(shù)
INCLUDE Irvine32.inc
.code
;------------------------------------------
PromptForIntegers PROC
; 提示用戶輸入整數(shù)數(shù)組
; Receives:
; ptrPrompt: PTR BYTE ; 提示信息
; ptrArray: PTR DWORD ; 數(shù)組首地址
; arraySize: DWORD ; 數(shù)組大小
; Returns: 無(wú)
;------------------------------------------
arraySize EQU [ebp + 16]
ptrArray EQU [ebp + 12]
ptrPrompt EQU [ebp + 8]
enter 0,0
pushad ; 保存所有寄存器
mov ecx, arraySize
cmp ecx, 0 ; 判斷數(shù)組長(zhǎng)度是否小于等于0
jle L2 ; 如果是則退出
mov edx, ptrPrompt ; 提示信息的地址
mov esi, ptrArray
L1:
call WriteString ; 顯示字符串
call ReadInt ; 從EAX讀取一個(gè)整數(shù)
call Crlf ; 換行
mov [esi], eax ; 存儲(chǔ)到數(shù)組
add esi, 4 ; 下一個(gè)整數(shù)
LOOP L1
L2:
popad ; 恢復(fù)所有寄存器
leave ; 恢復(fù)棧
ret 12
PromptForIntegers ENDP
END
_arraysum.asm
; 數(shù)組求和
INCLUDE Irvine32.inc
.code
;------------------------------------------
ArraySum PROC
; 計(jì)算數(shù)組元素之和
; Receives:
; ptrArray ; 數(shù)組首地址
; arraySize ; 數(shù)組大小
; Returns: EAX = sum
;------------------------------------------
ptrArray EQU [ebp + 8]
arraySize EQU [ebp + 12]
enter 0,0
push ecx ; 不要push EAX
push esi
mov eax, 0 ; 設(shè)置和為0
mov esi, ptrArray
mov ecx, arraySize
cmp ecx, 0 ; 數(shù)組大小是否小于0
jle L2 ; 如果是則退出
L1:
add eax, [esi] ; 添加一個(gè)整數(shù)到sum
add esi, 4 ; 指向數(shù)組下一個(gè)元素
LOOP L1 ; 循環(huán)
L2:
pop esi
pop ecx ; 返回sum到EAX中
leave
ret 8 ; 恢復(fù)棧
ArraySum ENDP
END
_display.asm
; 顯示程序
INCLUDE Irvine32.inc
.code
;------------------------------------------
DisplaySum PROC
; 在控制臺(tái)顯示數(shù)組和
; Receives:
; ptrPrompt ; 提示信息首地址
; theSum ; 數(shù)組和
; Returns: 無(wú)
;------------------------------------------
theSum EQU [ebp + 12]
ptrPrompt EQU [ebp + 8]
enter 0, 0
push eax
push edx
mov edx, ptrPrompt ; 指向提示信息
call WriteString
mov eax, theSum
call WriteInt ; 顯示EAX
call Crlf
pop edx
pop eax
leave
ret 8
DisplaySum ENDP
END
Sum_main.asm
; 數(shù)組求和 總模塊
; 使用到的模塊:_arraysum.asm, _display.asm, _prompt.asm
INCLUDE Irvine32.inc
EXTERN PromptForIntegers@0:PROC
EXTERN ArraySum@0:PROC, DisplaySum@0:PROC
; 重定義方法名,為了使用方便
ArraySum EQU ArraySum@0
PromptForIntegers EQU PromptForIntegers@0
DisplaySum EQU DisplaySum@0
; 數(shù)組長(zhǎng)度
Count = 3
.data
prompt1 BYTE "Enter a signed integer: ",0
prompt2 BYTE "The sum of the integers is: ",0
array DWORD Count DUP(?)
sum DWORD ?
.code
main PROC
call Clrscr
; 提示用戶輸入數(shù)據(jù)
push Count
push OFFSET array
push OFFSET prompt1
call PromptForIntegers
; 計(jì)算數(shù)組之和
push Count
push OFFSET array
call ArraySum
mov sum, eax
; 顯示和
push sum
push OFFSET prompt2
call DisplaySum
call Crlf
call WaitMsg
exit
main ENDP
END
運(yùn)行效果:
6). 使用INVOKE and PROTO 創(chuàng)建模塊
sum.inc
; (sum.inc)
INCLUDE Irvine32.inc
PromptForIntegers PROTO,
ptrPrompt:PTR BYTE, ; prompt string
ptrArray:PTR DWORD, ; points to the array
arraySize:DWORD ; size of the array
ArraySum PROTO,
ptrArray:PTR DWORD, ; points to the array
arraySize:DWORD ; size of the array
DisplaySum PROTO,
ptrPrompt:PTR BYTE, ; prompt string
theSum:DWORD ; sum of the array
__prompt.asm
; 提示用戶輸入整數(shù)
INCLUDE sum.inc ; 獲取方法原型
.code
;------------------------------------------
PromptForIntegers PROC,
ptrPrompt: PTR BYTE, ; 提示信息
ptrArray: PTR DWORD, ; 數(shù)組首地址
arraySize: DWORD ; 數(shù)組大小
; 提示用戶輸入整數(shù)數(shù)組
; Returns: 無(wú)
;------------------------------------------
pushad ; 保存所有寄存器
mov ecx, arraySize
cmp ecx, 0 ; 判斷數(shù)組長(zhǎng)度是否小于等于0
jle L2 ; 如果是則退出
mov edx, ptrPrompt ; 提示信息的地址
mov esi, ptrArray
L1:
call WriteString ; 顯示字符串
call ReadInt ; 從EAX讀取一個(gè)整數(shù)
call Crlf ; 換行
mov [esi], eax ; 存儲(chǔ)到數(shù)組
add esi, 4 ; 下一個(gè)整數(shù)
LOOP L1
L2:
popad ; 恢復(fù)所有寄存器
ret
PromptForIntegers ENDP
END
__arraysum.asm
; 數(shù)組求和
INCLUDE sum.inc
.code
;------------------------------------------
ArraySum PROC,
ptrArray:PTR DWORD, ; 數(shù)組首地址
arraySize:DWORD ; 數(shù)組大小
; 計(jì)算數(shù)組元素之和
; Returns: EAX = sum
;------------------------------------------
push ecx ; 不要push EAX
push esi
mov eax, 0 ; 設(shè)置和為0
mov esi, ptrArray
mov ecx, arraySize
cmp ecx, 0 ; 數(shù)組大小是否小于0
jle L2 ; 如果是則退出
L1:
add eax, [esi] ; 添加一個(gè)整數(shù)到sum
add esi, 4 ; 指向數(shù)組下一個(gè)元素
LOOP L1 ; 循環(huán)
L2:
pop esi
pop ecx ; 返回sum到EAX中
ret ; 恢復(fù)棧
ArraySum ENDP
END
__display.asm
; 顯示程序
INCLUDE Irvine32.inc
.code
;------------------------------------------
DisplaySum PROC,
ptrPrompt: PTR BYTE, ; 提示信息首地址
theSum:DWORD ; 數(shù)組和
; 在控制臺(tái)顯示數(shù)組和
; Returns: 無(wú)
;------------------------------------------
push eax
push edx
mov edx, ptrPrompt ; 指向提示信息
call WriteString
mov eax, theSum
call WriteInt ; 顯示EAX
call Crlf
pop edx
pop eax
ret
DisplaySum ENDP
END
Sum__main.asm
; 數(shù)組求和 總模塊
; 使用到的模塊:_arraysum.asm, _display.asm, _prompt.asm
INCLUDE sum.inc
; 數(shù)組長(zhǎng)度
Count = 3
.data
prompt1 BYTE "Enter a signed integer: ",0
prompt2 BYTE "The sum of the integers is: ",0
array DWORD Count DUP(?)
sum DWORD ?
.code
main PROC
call Clrscr
INVOKE PromptForIntegers, ADDR prompt1, ADDR array, Count
INVOKE ArraySum, ADDR array, Count
mov sum, eax
INVOKE DisplaySum, ADDR prompt2, sum
call Crlf
call WaitMsg
exit
main ENDP
END
注:這兩種方式柑土,使用較多的為EXTERN
創(chuàng)建多模塊方式蜀肘,第二種在32位中使用較多。