匯編開(kāi)發(fā)(六):程序進(jìn)階

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
值傳遞.png
  • 引用傳遞
    向棧中壓入變量的地址
push OFFSET val2
push OFFSET val1
call Swap  
引用傳遞.png
  • 傳遞數(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
Accessing Stack Parameters.png
  • 顯示堆棧參數(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
Stack frame for the MySub procedure.png
5). 局部變量

局部變量在運(yùn)行棧上創(chuàng)建线梗,通常在基址(EBP)之下。

Local Variables.png
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
Stack frame after creating local variables.png
  • 局部變量符號(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
Stack frame before and after ENTER has executed.png
  • 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 
Stack Frame and Registers (CalcSum).png
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
Recursive calls to the factorial function.png

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]
Argument types used with INVOKE.png

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]

Attributes field in the PROC directive.png
  • 參數(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 
ArraySum.png
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 
效果.png
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 
效果.png

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í)指令INVOKEPROTO拆讯,隱藏更多的低級(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
  • 使用INCLUDEEXTERNDEF

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):

Structure chart, ArraySum program.png
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)行效果:

效果.png
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位中使用較多。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稽屏,一起剝皮案震驚了整個(gè)濱河市扮宠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狐榔,老刑警劉巖坛增,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異薄腻,居然都是意外死亡收捣,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門庵楷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)罢艾,“玉大人,你說(shuō)我怎么就攤上這事尽纽「莉牵” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵弄贿,是天一觀的道長(zhǎng)春锋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)差凹,這世上最難降的妖魔是什么期奔? 我笑而不...
    開(kāi)封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮危尿,結(jié)果婚禮上能庆,老公的妹妹穿的比我還像新娘。我一直安慰自己脚线,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布弥搞。 她就那樣靜靜地躺著邮绿,像睡著了一般渠旁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上船逮,一...
    開(kāi)封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天顾腊,我揣著相機(jī)與錄音,去河邊找鬼挖胃。 笑死杂靶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酱鸭。 我是一名探鬼主播吗垮,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼凹髓!你這毒婦竟也來(lái)了烁登?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蔚舀,失蹤者是張志新(化名)和其女友劉穎饵沧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赌躺,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狼牺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了礼患。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片是钥。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖讶泰,靈堂內(nèi)的尸體忽然破棺而出咏瑟,到底是詐尸還是另有隱情,我是刑警寧澤痪署,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布码泞,位于F島的核電站,受9級(jí)特大地震影響狼犯,放射性物質(zhì)發(fā)生泄漏余寥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一悯森、第九天 我趴在偏房一處隱蔽的房頂上張望宋舷。 院中可真熱鬧,春花似錦瓢姻、人聲如沸祝蝠。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)绎狭。三九已至细溅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間儡嘶,已是汗流浹背喇聊。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹦狂,地道東北人誓篱。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像凯楔,于是被迫代替她去往敵國(guó)和親窜骄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 原文地址:C語(yǔ)言函數(shù)調(diào)用棧(一)C語(yǔ)言函數(shù)調(diào)用棧(二) 0 引言 程序的執(zhí)行過(guò)程可看作連續(xù)的函數(shù)調(diào)用啼辣。當(dāng)一個(gè)函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,629評(píng)論 1 19
  • 一啊研、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡(jiǎn)單分配策略的問(wèn)題地址空間不隔離內(nèi)存使用效率低程序運(yùn)行的地址不確定 關(guān)于...
    SeanCST閱讀 7,819評(píng)論 0 27
  • 組件 計(jì)算機(jī)是一種數(shù)據(jù)處理設(shè)備,它由CPU和內(nèi)存以及外部設(shè)備組成鸥拧。CPU負(fù)責(zé)數(shù)據(jù)處理党远,內(nèi)存負(fù)責(zé)存儲(chǔ),外部設(shè)備負(fù)責(zé)數(shù)...
    哆啦灬少A夢(mèng)閱讀 1,594評(píng)論 1 2
  • 一個(gè)家兩個(gè)人富弦,能走在一起實(shí)屬緣分沟娱,如果相處非常容易,就不會(huì)有說(shuō)好好經(jīng)營(yíng)婚姻的各種話題腕柜。 別人都說(shuō)济似,能溝通就不冷戰(zhàn)...
    微女神風(fēng)鈴兒閱讀 190評(píng)論 0 0
  • 十二月一號(hào),年末月始盏缤,新的開(kāi)始也是舊的結(jié)束砰蠢。來(lái)到廈門細(xì)細(xì)算來(lái)也差不多一個(gè)月了,從找房子到找工作將近十天唉铜,住過(guò)最好的...
    將心比甜閱讀 182評(píng)論 0 0