《匯編語言程序設(shè)計簡明教程》第5章子程序_第1頁
《匯編語言程序設(shè)計簡明教程》第5章子程序_第2頁
《匯編語言程序設(shè)計簡明教程》第5章子程序_第3頁
《匯編語言程序設(shè)計簡明教程》第5章子程序_第4頁
《匯編語言程序設(shè)計簡明教程》第5章子程序_第5頁
已閱讀5頁,還剩151頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1匯編語言程序設(shè)計

簡明教程2第五章 子程序

5.1 子程序

5.2 參數(shù)的傳遞

5.3 嵌套和遞歸子程序

5.4 多模塊程序設(shè)計

5.5 匯編語言與C語言混合編程

5.6 DOS和BIOS調(diào)用 習(xí)題五3子程序(Subroutine

): 把程序需要完成的任務(wù)分解為若干個“子任務(wù)”,每個“子任務(wù)”由一段相對獨立的程序完成,稱為“子程序”。調(diào)用子程序的程序稱為“主程序”或者“主調(diào)程序”。子程序也稱為“過程”(Procedure),在高級語言里還稱作“函數(shù)”(Function)。4子程序結(jié)構(gòu)程序的優(yōu)點:1.程序結(jié)構(gòu)清晰,提高了程序的可閱讀性和可維護(hù)性。2.每個子程序可以獨立地進(jìn)行調(diào)試,由于程序規(guī)模較小,降低了調(diào)試難度。3.每個子程序就是一個具有特定功能的獨立的程序,提高程序的“可重用性”,提高了軟件開發(fā)效率。5圖5-1子程序的調(diào)用和返回

6近程子程序:只能被同一個代碼段里的程序調(diào)用的子程序。由于主程序和子程序處于同一個代碼段,調(diào)用和返回時只需要改變IP寄存器的值,CS寄存器的值保持不變。近程子程序的入口地址用16位段內(nèi)偏移地址表示。

7遠(yuǎn)程子程序:能夠被不同代碼段的程序調(diào)用,也能被同一代碼段的程序調(diào)用的子程序。調(diào)用這樣的子程序時,需要同時改變CS和IP寄存器的值,返回時,需要從堆棧里彈出32位的返回地址送入IP,CS寄存器。遠(yuǎn)程子程序的入口地址用16位段基址和16位段內(nèi)偏移地址表示。

子程序的類型在定義時說明

8

5.1 子程序

5.1.1CALL和RET指令

5.1.2子程序的定義5.1.3子程序文件5.1.4子程序應(yīng)用9CALL(Call,調(diào)用)指令

段內(nèi)直接調(diào)用 格式:CALL 子程序名操作:SP←SP-2,SS:[SP]←IP(保存16位返回地址)

IP←子程序入口的偏移地址例如:CALL PROC1

段內(nèi)間接調(diào)用

格式:CALL REG16/MEM16

操作:SP←SP-2,SS:[SP]←IP(保護(hù)16位返回地址)

IP←REG16/MEM16

5.1.1

CALL和RET指令

10例如:調(diào)用名為“PROC1”的近程子程序。(1) CALL PROC1(2) LEA CX, PROC1 CALL CX (3)ADDR_PROC1DWPROC1

;子程序偏移地址放入存儲器字變量

……

CALL ADDR_PROC1 ;調(diào)用近程子程序PROC1(4) …… LEA BX,ADDR_PROC1 CALL WORDPTR[BX] ;調(diào)用近程子程序PROC111段間直接調(diào)用

格式:CALLFARPTR 子程序名操作:SP←SP-2,SS:[SP]←CS SP←SP-2,SS:[SP]←IP

(保存32位返回地址,偏移地址保存在較小地址處)

IP←子程序入口的偏移地址,

CS←子程序入口的段基址CALLFARPTRPROC2

例如:12段間間接調(diào)用格式:CALL MEM32操作:SP←SP-2, SS:[SP]←CS SP←SP-2, SS:[SP]←IP IP←[MEM32],CS←[MEM32+2]例如:ADD_PROC2 DD PROC2 ;子程序入口地址放入存儲器雙字變量

……

CALL ADD_PROC2;調(diào)用遠(yuǎn)程子程序PROC213無參數(shù)段內(nèi)返回格式:RET操作:IP←SS:[SP],SP←SP+2

有參數(shù)段內(nèi)返回格式:RETD16操作:IP←SS:[SP],SP←SP+2SP←SP+D16RET(Return,返回)指令14無參數(shù)段間返回

格式:RET操作:IP←SS:[SP],SP←SP+2 CS←SS:[SP],SP←SP+2有參數(shù)段間返回格式:RETD16操作:IP←SS:[SP],SP←SP+2 CS←SS:[SP],SP←SP+2 SP←SP+D16155.1.2子程序的定義子程序名 PROC [NEAR/FAR]

子程序體子程序名

ENDP

16

說明:子程序名應(yīng)為合法的標(biāo)識符,子程序名不能與同一個源程序中的標(biāo)號、變量名、其它子程序名相同。方括號中的內(nèi)容是子程序的遠(yuǎn)近屬性選項,二者可選其一,如果缺省,默認(rèn)為NEAR。用NEAR說明的子程序是“近程子程序”,它只能被與它同一代碼段的程序調(diào)用。用FAR說明的子程序是“遠(yuǎn)程子程序”,它不僅能被與它同一代碼段的程序調(diào)用,也能被其它代碼段的程序調(diào)用。子程序的定義要寫在代碼段內(nèi)。17ZEROBYTES PROC ;定義一個子程序

XOR AX,AX ;AX清零

MOV CX,128 ;循環(huán)次數(shù)送CXZEROLOOP:MOV[BX],AX ;將一個字存儲單元清零

ADD BX,2 ;修改地址

LOOP ZEROLOOP ;循環(huán)控制

RET ;返回主程序ZEROBYTES ENDP ;子程序結(jié)束思考題:該子程序完成了什么功能?調(diào)用該子程序時,

應(yīng)該先做什么準(zhǔn)備工作?18ZEROBYTES:XORAX,AX ;AX寄存器清零

MOV CX,128 ;計數(shù)器CX置初值ZEROLOOP: MOV [BX],AX ;一個字單元清零

ADD BX,2 ;修改地址指針,指向下一個字

LOOP ZEROLOOP ;循環(huán)控制

RET ;結(jié)束程序運行,返回主程序子程序也可以簡單地寫成下面的形式:缺點:邊界不容易清晰地區(qū)分;只能定義“近程子程序”;

只能被同一代碼段內(nèi)的程序調(diào)用。19CODE SEGMENT ;代碼段開始

MAIN PROCFAR ;主程序開始

…… ;主程序的指令序列

MOV AX,4C00H INT 21H ;返回DOSMAIN ENDP ;主程序結(jié)束

…… ;其它程序CODE ENDS ;代碼段結(jié)束

END MAIN ;源程序結(jié)束用戶編寫的“主程序”也可以看作是由操作系統(tǒng)調(diào)用的一個子程序:20CODE SEGMENT ;代碼段開始MAIN PROCFAR ;主程序開始

PUSH DS ;操作系統(tǒng)的返回點在DS:0 XOR AX,AX PUSH AX ;把32位返回點地址壓入堆棧

…… ;主程序的指令序列

RET ;返回DOSMAIN ENDP ;主程序結(jié)束

…… ;其它程序CODE ENDS ;代碼段結(jié)束

END MAIN ;源程序結(jié)束還可以這樣寫:21[例5.1]子程序FRACTOR用來計算一個數(shù)的階乘。主程序利用它

計算1~5的階乘,存入FRA數(shù)組。

.386DATA SEGMENT USE16FRA DW 5DUP(?)DATA ENDS22CODE SEGMENT USE16ASSUMECS:CODE,DS:DATASTART:MOV AX,DATA MOV DS,AX MOV EBX,1 ;BX中存放待求階乘的數(shù)

MOV CX,5 ;求階乘次數(shù)(循環(huán)次數(shù))LOOP0:CALL FRACTOR ;調(diào)用FRACTOR求階乘

MOV FRA[2*EBX-2],AX ;保存結(jié)果(階乘)

INC BX ;產(chǎn)生下一個待求階乘的數(shù)

LOOP LOOP0 ;循環(huán)控制

MOV AX,4C00H INT 21H23FRACTOR PROC NEAR MOV CX,BX ;待求階乘的數(shù)轉(zhuǎn)入CX寄存器

MOV AX,1 ;“累乘器”置初值1 FRALOOP: MUL CX ;累乘

LOOP FRALOOP ;循環(huán)控制

RETFRACTOR ENDPCODE ENDS END START思考題:這個程序存在問題,是什么問題?如何解決?24FRACTOR PROC NEAR PUSH CX ;CX壓入堆棧保護(hù)

MOV CX,BX ;待求階乘的數(shù)轉(zhuǎn)入CX寄存器

MOV AX,1 ;“累乘器”置初值1 FRALOOP: MUL CX ;累乘

LOOP FRALOOP ;循環(huán)控制

POP CX ;從堆棧里彈出CX的原值

RETFRACTOR ENDPCODE ENDS END START

在子程序入口處把相關(guān)寄存器的值入棧保護(hù),程序返回前再恢復(fù)它們的值,這個操作稱為“保護(hù)現(xiàn)場”和“恢復(fù)現(xiàn)場”。25注意:上面的程序使用了32位尋址方式,連接時在TLINK命令中要增加“/3”選擇項:

TLINK/3XXXXXX

(XXXXXX為匯編得到的目標(biāo)文件名)26子程序的基本格式:

子程序名 PROC [NEAR/FAR] PUSH …… ;保護(hù)現(xiàn)場(寄存器/存儲器)

PUSH …… ;個數(shù)根據(jù)具體情況決定

……

;子程序主體

…… POP ……

;恢復(fù)現(xiàn)場,注意出棧次序

POP …… ;先進(jìn)棧的寄存器后出棧

RET ;返回

子程序名

ENDP275.1.3子程序文件設(shè)計一個子程序之前,首先應(yīng)該明確:子程序的名字;子程序的功能;入口參數(shù):為了運行這個子程序,主程序為它準(zhǔn)備了哪幾個“已知條件”?這些參數(shù)存放在什么地方?出口參數(shù):這個子程序的運行結(jié)果有哪些?存放在什么地方?影響寄存器:執(zhí)行這個子程序會改變哪幾個寄存器的值?其它需要說明的事項。

上述內(nèi)容連同子程序源代碼等合稱為“子程序文件”。常常把上述內(nèi)容以“程序注釋”的方式書寫在一個子程序的首部。28;名稱:Square;功能:求16Bit無符號數(shù)的平方根;入口參數(shù):16Bit無符號數(shù)在AX中;出口參數(shù):8Bit平方根數(shù)在AL中;影響寄存器:AX(AL)例如,一個名為“SQUARE”的子程序,用來求一個數(shù)的平方根,

源程序如下:29SQUARE PROC NEAR PUSH CX ;保護(hù)現(xiàn)場

PUSH BX MOV BX,AX ;要求平方根的數(shù)送BX MOV AL,0 ;AL中存放平方根,初值0 MOV CX,1 ;CX置入第一個奇數(shù)1 ;利用公式:N^2=1+3+……+(2N-1)求平方根30NEXT: SUB BX,CX JB DONE ADD CX,2 ;形成下一個奇數(shù)

INC AL ;AL存放已減去奇數(shù)的個數(shù)

JMP NEXTDONE: POP BX ;恢復(fù)現(xiàn)場

POP CX RET ;返回SQUARE ENDP課內(nèi)練習(xí):編寫主程序,利用SQUARE子程序,求數(shù)26

的平方根,存放在變量ROOT中。315.1.4子程序應(yīng)用每調(diào)用一次子程序,主程序需要做三件事:(1)為子程序準(zhǔn)備入口參數(shù)(2)調(diào)用子程序(3)處理子程序的返回參數(shù)32DATA SEGMENTX DW 59,3500,139,199,77;欲求平方根的數(shù)組ROOT DB 5DUP(?) ;存放平方根內(nèi)存區(qū)DATA ENDSCODE SEGMENTASSUME CS:CODE,DS:DATASTART:MOV AX,DATA MOV DS,AX[例],為了求5個無符號數(shù)的平方根,編制主程序如下:33

LEA BX,X ;初始化指針

LEA SI,ROOT MOV CX,5 ;設(shè)置計數(shù)器初值ONE: MOV AX,[BX] ;設(shè)置入口參數(shù)

CALL SQUARE ;調(diào)用子程序

MOV [SI],AL ;保存返回參數(shù)(平方根)

ADD BX,2 ;修改指針

INC SI ;修改指針

LOOP ONE ;循環(huán)控制

MOV AX,4C00H ;返回DOS INT 21H34[例5.2] 從鍵盤上輸入10個十進(jìn)制數(shù),從中找出最大的數(shù),在屏幕上顯示出來。INCLUDE YLIB.H ;聲明外部函數(shù).DATA NUM DW 10 DUP(?)PROMPT1 DB 0AH,0DH,“InputaNumber:$”PROMPT2 DB 0AH,0DH,“TheMaximumNumberis:$”35.CODESTART:MOV AX,@DATA MOV DS,AX MOV CX,10 LEA BX,NUMNEXT:LEA DX,PROMPT1

;設(shè)置READINT子程序的入口參數(shù)

CALL READINT MOV [BX],AX ;保存讀入的十進(jìn)制數(shù)

ADD BX,2 LOOP NEXT36

LEA BX,NUM ;為子程序MAX準(zhǔn)備入口參數(shù)

MOV CX,10 CALLMAX

;MAX子程序找出N個數(shù)中最大的數(shù),并將此數(shù)從AX返回

LEA DX,PROMPT2 CALL WRITEINT ;在屏幕上顯示AX中的數(shù)

MOV AX,4C00H INT 21H 37 ;

子程序MAX ;功能:求若干個數(shù)中的最大值

;入口參數(shù):BX=第一個數(shù)據(jù)的偏移地址,CX=數(shù)據(jù)個數(shù)

;

出口參數(shù):最大值在AX中38MAX PROC MOV AX,8000H ;最小的16位有符號數(shù)AGAIN:CMP AX,[BX] JGE SKIP MOV AX,[BX] ;將當(dāng)前最大數(shù)送AXSKIP:INC BX INC BX LOOP AGAIN RET ;AX返回最大數(shù)MAX ENDP END START395.2 參數(shù)的傳遞 傳遞的參數(shù)有兩種類型:(1)值傳遞:把參數(shù)的值放在約定的寄存器或存儲單元傳遞給子程序,或者,由子程序返回給主程序。

如果一個入口參數(shù)是用值傳遞的,子程序可以使用這個值,但是無法改變它自身的值。(2)地址傳遞:把參數(shù)事先存放在某個存儲單元,把這個存儲單元的地址作為參數(shù)傳遞給子程序。

如果一個參數(shù)使用它的地址來傳遞,子程序可以改變這個參數(shù)的值。例如,把存放結(jié)果的存儲單元的地址作為“入口參數(shù)”傳遞給子程序,子程序可以把運算結(jié)果直接存入這個單元。40參數(shù)存放在寄存器中參數(shù)存放在主、子程序“共享”的數(shù)據(jù)段內(nèi)參數(shù)存放在堆棧內(nèi)參數(shù)的存放位置,有三種情形:

41用寄存器傳遞參數(shù)

.MODEL SMALL.DATAFIBLST DW 1,1,18DUP(?)N DW 20[例5.3] 求菲波那契數(shù)列的前n項。菲波那契數(shù)列的前兩項為1,1,以后的每一項都是其前兩項之和。

X0=1,X1=1,Xi=Xi-1+Xi-2(i>=2)

本例使用寄存器AX,BX來傳遞參數(shù),傳遞的是參數(shù)的值。42.CODESTART: MOV AX,@DATA MOV DS,AX LEA SI,FIBLST MOV CX,N SUB CX,2ONE: MOV AX,[SI] MOV BX,[SI+2] CALL FIB MOV [SI+4],AX ADD SI,2 LOOP ONE MOV AX,4C00H INT 21H 43;子程序FIB ;功能:求菲波那契數(shù)列數(shù)列的一項 ;入口參數(shù):AX=Xi-1,BX=Xi-2

;出口參數(shù):AX=Xi

=Xi-1+Xi-2

FIB PROC ADD AX,BX RETFIB ENDP END START44 .MODEL SMALL .DATA FIBLST DW 1,1,18DUP(?) N DW 20 下面的程序仍然利用寄存器傳遞參數(shù),但是傳遞的是參數(shù)的地址。45.CODESTART:MOV AX,@DATA MOV DS,AX LEA SI,FIBLST MOV CX,N SUB CX,2ONE: CALL FIB ADD SI,2 LOOP ONE MOV AX,4C00H INT 21H46 FIB PROC PUSH AX MOV AX,[SI] ;取XI-2 ADD AX,[SI+2] ;加XI-1 MOV [SI+4],AX ;存入XI POP AX RET FIB ENDP END START;子程序FIB;

功能:求菲波那契數(shù)列數(shù)列的一項;入口參數(shù):SI=Xi-2的段內(nèi)偏移地址;出口參數(shù):無(結(jié)果已由子程序存入數(shù)組內(nèi))

由于傳遞的是參數(shù)的地址,子程序根據(jù)這個地址取出Xi-2和Xi-1,計算得到的結(jié)果直接存入變量Xi中。47 .MODEL SMALL .DATA FIBLSTDW 1,1,18DUP(?) N DW 20 XI1 DW ? ;存放XI-1 XI2 DW ? ;存放XI-2 XI DW ? ;存放XI .CODE START: MOV AX,@DATA MOV DS,AX2.變量(共享數(shù)據(jù)段)傳遞參數(shù)

仍以例5.3為例,程序作如下修改:48

LEA SI,FIBLST ;設(shè)置地址指針

MOV CX,N SUB CX,2 ;設(shè)置計數(shù)器初值ONE: MOV AX,[SI+2] MOV XI1,AX ;Xi-1置入XI1 MOV AX,[SI] MOV XI2,AX ;Xi-2置入XI2 CALL FIB ;調(diào)用子程序

MOV AX,XI ;取出子程序計算結(jié)果Xi MOV [SI+4],AX ;置入FIBLST數(shù)組

ADD SI,2 ;修改地址指針

LOOP ONE ;循環(huán)控制

MOV AX,4C00H INT 21H49;子程序FIB;功能:計算菲波那契數(shù)列數(shù)列的一項;入口參數(shù):XI1=XI-1,XI2=XI-2;出口參數(shù):XI=XI-1+XI-250FIB PROC PUSH AX ;保護(hù)現(xiàn)場

MOV AX,XI1 ;AX=XI-1 ADD AX,XI2 ;AX=XI-1+XI-2 MOV XI,AX ;XI←AX POP AX ;恢復(fù)現(xiàn)場

RETFIB ENDP END START51.MODEL SMALL.DATA FIBLST DW 1,1,18DUP(?) N DW 20 .CODESTART: MOV AX,@DATA MOV DS,AX LEA SI,FIBLST MOV CX,N SUB CX,23.堆棧傳遞參數(shù)仍以例5.3為例,程序作如下修改:52ONE: PUSH AX ;為保存結(jié)果,在堆?!邦A(yù)留”單元

PUSH WORDPTR[SI] ;XI-2入棧

PUSH WORDPTR[SI+2] ;XI-1入棧 CALL FIB ;調(diào)用子程序,執(zhí)行后堆棧狀態(tài)1 POP AX ;從堆棧彈出結(jié)果,執(zhí)行后堆棧狀態(tài)4 MOV [SI+4],AX ;把結(jié)果存入FIBLST數(shù)組

ADD SI,2 LOOP ONE MOV AX,4C00H INT 21H53;子程序FIB;功能:計算菲波那契數(shù)列數(shù)列的一項;入口參數(shù):XI-1,XI-2在堆棧中;出口參數(shù):XI在棧頂

FIB PROC PUSH BP MOV BP,SP ;堆棧狀態(tài)2

MOV AX,[BP+4] ;從堆棧取出XI-1 ADD AX,[BP+6] ;AX=XI-1+XI-2 MOV [BP+8],AX ;結(jié)果存入堆棧

POP BP ;恢復(fù)BP RET 4 ;返回,SP+4,執(zhí)行后堆棧狀態(tài)3

FIB ENDP END START54調(diào)用子程序過程中堆棧狀態(tài)的變化555.3 嵌套和遞歸子程序5.3.1 子程序嵌套

5.3.2遞歸子程序565.3.1 子程序嵌套

被調(diào)用的子程序在返回前又調(diào)用了其他子程序,稱為子程序嵌套。

(也稱過程嵌套NestProcedureCall)57

;主程序 ……. CALL SUBA ;①

…….;子程序SUBA

……. CALL SUBB ;②

……. RET;子程序SUBB

……. RET58執(zhí)行2條CALL指令后堆棧狀態(tài)

執(zhí)行SUBB中ret指令后堆棧狀態(tài)

返回SUBA的地址←SP返回主程序的地址

返回SUBA的地址

返回主程序的地址←SP

595.3.2遞歸子程序

遞歸過程(RecursiveProcedure):一個直接或間接調(diào)用自身的過程。

如果一個問題可以分解為幾個子問題,而這些子問題又和原問題有相同的算法時,遞歸過程是非常方便的表達(dá)方式。 遞歸過程可以使用堆棧傳遞參數(shù),也可以用寄存器或存儲單元傳遞參數(shù)。60計算1~N自然數(shù)之和可以定義為:SUM(N)= N=1時,SUM函數(shù)直接返回1。

N>1時,計算SUM(N)分成兩步:也可以定義為:SUM(N)=1+2+3+……+N

計算SUM(N-1):設(shè)置入口參數(shù)為N-1,

調(diào)用SUM函數(shù)(調(diào)用自身)進(jìn)行加法運算:N+SUM(N-1)61.MODEL SMALL.STACK 100 ;定義100字節(jié)用作堆棧.CODETOTAL DW ?START: MOV CX,3 ;為子程序準(zhǔn)備參數(shù)

CALL SUM ;調(diào)用子程序SUM MOV CS:TOTAL,AX;保存結(jié)果

MOV AX,4C00H INT 21H ;返回62;遞歸過程SUM;入口參數(shù):CX=N,出口參數(shù):AX=1~N的和SUM PROC PUSH CX ;保存入口參數(shù)(N)

CMP CX,1 ;判CX(N)是否為1 JE BACK ;CX(N)為1,轉(zhuǎn)BACK DEC CX ;CX-1,為下一次調(diào)用準(zhǔn)備參數(shù)

CALL SUM ;遞歸調(diào)用,計算1~N-1的和

INC CX ;恢復(fù)CX為N ADD AX,CX ;加到AX中

JMP EXITBACK: MOV AX,1 ;N=1時,通過AX返回1EXIT: POP CX ;恢復(fù)入口參數(shù)

RETSUM ENDP END START63

這個程序使用寄存器進(jìn)行參數(shù)的傳遞。主程序中的“CALLSUM”指令執(zhí)行了一次。子程序SUM中的指令“CALL SUM”執(zhí)行了2次。

CALL指令先后共執(zhí)行了3次,先后3次進(jìn)入同一個子程序

SUM。64遞歸調(diào)用和返回示意圖

編寫遞歸程序時,必須保證要有一個終止條件,而且,經(jīng)過有限次執(zhí)行這個子程序,這個終止條件能夠?qū)崿F(xiàn)。否則,程序?qū)a(chǎn)生“死循環(huán)”或者其它的錯誤狀態(tài)。65.MODEL SMALL.STACK 100 ;定義100字節(jié)用作堆棧.CODETOTAL DW ?START: PUSH AX ;為返回結(jié)果預(yù)留單元, ;AX中內(nèi)容無意義

MOV AX,3 ;為子程序準(zhǔn)備參數(shù)

PUSH AX ;入口參數(shù)壓入堆棧

CALL SUM ;調(diào)用子程序SUM POP CS:TOTAL ;③保存結(jié)果

MOV AX,4C00H INT 21H ;返回

高級語言中的遞歸程序普遍使用堆棧傳遞參數(shù)。上面的例子改寫如下:66;遞歸過程SUM;入口參數(shù):[SP+2]=N,出口參數(shù):1~N的和在堆棧棧頂;影響寄存器:AX,CX67SUM PROC PUSH BP MOV BP,SP ;① MOV CX,[BP+4] ;取出入口參數(shù)N,存入CX CMP CX,1 ;N=1? JE BACK ;N=1,轉(zhuǎn)BACK DEC CX ;CX-1,為下一次調(diào)用準(zhǔn)備參數(shù)

PUSH AX ;為保留下一次調(diào)用結(jié)果預(yù)留單元 ;AX中內(nèi)容無意義

PUSH CX ;入口參數(shù)(N-1)壓入堆棧

CALL SUM ;遞歸調(diào)用,計算1~N-1的和68

POP AX ;從堆棧彈出子程序運算結(jié)果(1~N-1的和) ADD AX,[BP+4] ;把N加到AX中,得到1~N的和

MOV [BP+6],AX ;1~N的和存入堆棧預(yù)留單元

JMP EXITBACK:MOV WORDPTR[BP+6],1 ;N=1時,返回1EXIT:POP BP RET 2SUM ENDP END START6970715.4 多模塊程序設(shè)計

5.4.1 段的完整定義5.4.2簡化段定義5.4.3創(chuàng)建多模塊程序721.一個應(yīng)用程序的開發(fā)由一個小組,而不是一個人完成的。

每個程序員編制的源程序自然地構(gòu)成一個(或者是多個)源程序文件。

每個源程序文件內(nèi)包括了一個,或多個“子程序”。這些源程序文件常常被稱為模塊(Module)。

有時候也把位于同一個源程序文件內(nèi)的各個“子程序”,或者一個“數(shù)據(jù)段”定義等稱為模塊。

一個可供實際使用的應(yīng)用程序,它的源程序基本上都是由若干個程序文件組成的。732.源程序文件規(guī)模過大,會造成管理,維護(hù)上的困難。

即便是一個人開發(fā)的應(yīng)用程序,也應(yīng)把一個大的程序分解成多個源代碼文件。

這樣,每個文件長度適中,容易閱讀和維護(hù),如果修改了某個模塊,只需對該模塊重新匯編,然后再和其他模塊鏈接,無須把每個模塊重新匯編一次。

3.許多“子程序”可以重復(fù)使用,獨立為一個源程序文件便于“子程序庫”的管理。

745.4.1 段的完整定義段名 SEGMENT[對齊方式][組合方式][使用類型][‘類’]

語句段名 ENDS完整的段定義格式如下: SEGMENT偽指令所在行內(nèi),方括號括起來的內(nèi)容稱為“可選項”,它的出現(xiàn)與否可以由使用者根據(jù)需要決定。751.對齊方式一個新的段開始時,對齊方式通知連接程序怎樣確定這個段的起始地址。對齊方式可以有下面幾種選擇:BYTE

使本段從前面段結(jié)束之后的下一個字節(jié)地址開始。WORD

使本段從前面段結(jié)束之后的下一個字地址開始。DWORD

使本段從前面段結(jié)束之后的下一個雙字地址開始。PARA

使本段從前面段結(jié)束之后的下一個節(jié)的地址(16的整數(shù)倍)開始存放,是默認(rèn)的對齊方式。如果沒有出現(xiàn)對齊方式說明,自動按照“PARA”來對齊。PAGE

使本段從前面段結(jié)束之后的下一個頁地址(256的整數(shù)倍)開始。

76假設(shè)前面一個段的結(jié)束地址為30204H,用不同對齊方式的結(jié)果如下表所示。對齊方式段基址起始偏移地址段間空隙(字節(jié))BYTE3020H0005H0WORD3020H0006H1DWORD3020H0008H3PARA3021H0000H11PAGE3030H0000H251772. 組合方式

組合方式通知連接程序以何種方式處理不同程序文件中名稱相同的段。組合方式可以有下面幾種選擇:PRIVATE

這個段不與其他同名段合并,每個段都有自己的段基地址,是默認(rèn)的組合方式。PUBLIC

將該段和其他名稱相同,組合方式同為“PUBLIC”的段前后連接在一起,合并成一個段,產(chǎn)生一個新的段基地址,所有標(biāo)號的偏移地址都進(jìn)行調(diào)整。STACK

將所有的STACK段連接為一個新的STACK段,類似于PUBLIC。COMMON所有同名的段使用相同的起始地址,這樣,這些段“共享”這一片共同的存儲區(qū)間。AT表達(dá)式 段的起始地址由表達(dá)式的值來指定,用于設(shè)定一些特殊的段。78使用(USE)類型

使用類型僅僅使用于使用80386以上指令系統(tǒng)的匯編程序(例如,在程序首部使用.386說明符) 使用16位尋址方式時,段內(nèi)偏移地址16位,每個段最大64KB。運行在實模式下的程序使用16位段。 使用32位尋址方式時,段內(nèi)偏移地址為32位,一個段最大可達(dá)4GB。運行在保護(hù)模式下的程序使用32位段。 使用80386以上CPU,并且工作在實模式下的程序不能遺漏“USE16”說明。

794.類名稱

一個段除了有一個段名之外,還可以有一個類名稱。類名稱是以引號引起來的任意字符串。 類名稱相同的段被放在安置在一片相鄰的存儲區(qū)間,但不會合并成同一個段。80CSEGSEGMENT 'CODE'ASSUMECS:CSEG,DS:DATA1,SS:MYSTACKMAIN PROC MOV AX,DATA1 MOV DS,AX MOV AX,SEGVAL2 MOV ES,AX

MOV AX,VAL1 LEA SI,VAL2 MOV BX,ES:[SI] [例5.5]本例中有兩個代碼段、兩個數(shù)據(jù)段和一個堆棧段,81 CMP AX,BX JB L1 CALL FARPTRSUB1 L1: MOV AX,4C00H INT 21HMAIN ENDPCSEG ENDS

;==========================================SUBCODE SEGMENT ‘CODE’ASSUMECS:SUBCODE,DS:DATA1,SS:MYSTACKSUB1 PROC FAR ; 過程體

RETSUB1 ENDPSUBCODE ENDS82;==================================

DATA1 SEGMENT 'DATA';類名稱為DATA VAL1 DW 1001HDATA1 ENDS;===================================

DATA2 SEGMENT 'DATA’;類名稱為DATA VAL2 DW 1002HDATA2 ENDS;===================================MYSTACK SEGMENT PARA STACK'STACK' DB 100HDUP(?)MYSTACK ENDS;===================================

END MAIN83

主過程main與子過程sub1分別在不同的代碼段中,它們的類名稱一樣;還有兩個數(shù)據(jù)段。經(jīng)過鏈接,從對應(yīng)的MAP文件中可以看到程序有兩個代碼段、兩個數(shù)據(jù)段和一個堆棧段:

Start Stop Length NameClass00000H00022H 00023H CSEGCODE00030H00030H 00001H SUBCODECODE00040H00041H 00002H DATA1DATA00050H00051H 00002H DATA2DATA00060H0015FH 00100H MYSTACKSTACK由于使用默認(rèn)的“PARA”對齊方式,段間有間隙。同類型名的段相鄰存放。84

如果將SUBCODE的段名改為CSEG,將DATA2段名改為DATA1,則鏈接器創(chuàng)建的MAP文件是這樣的:

Start Stop Length NameClass00000H00023H 00024H CSEGCODE00030H00033H 00004H DATA1DATA00040H0013FH 00100H MYSTACKSTACK由此可見,在同一個程序文件中,相同段名的代碼段合并成一個段,兩個同名的數(shù)據(jù)段也合并成一個段。855.4.2 簡化段定義

1.內(nèi)存模式定義偽指令使用簡化段定義時,需要首先定義所使用的內(nèi)存模式等參數(shù)。.MODEL偽指令決定程序的內(nèi)存模式類型、語言類型、操作系統(tǒng)類型和堆棧選項,格式如下:.MODEL內(nèi)存模式

[,語言類型][,操作系統(tǒng)類型][,堆棧選項]86內(nèi)存模式:微型(tiny)數(shù)據(jù)段和代碼段放在同一個段,整個程序只

有一個段。此模式由.com程序使用。小型(small)一個代碼段和一個數(shù)據(jù)段。中型(medium)可以有多個代碼段但只有一個數(shù)據(jù)段。緊湊(compact)一個代碼段和多個數(shù)據(jù)段。大型(large)多個代碼段和多個數(shù)據(jù)段。靜態(tài)數(shù)據(jù)

(不能改變的數(shù)據(jù))仍限制在64KB內(nèi)。巨型(huge)基本與大型同,只是靜態(tài)數(shù)據(jù)項長度可

以超過64KB。平坦(flat)保護(hù)模式。代碼和數(shù)據(jù)使用32位偏移,所有的代碼和數(shù)據(jù)都在一個32位段中。如果要編寫在Windows下運行的程序,一定要使用這種模式。87語言類型:C,BASIC,FORTRAN和PASCAL。這個關(guān)鍵字使匯編語言程序員能創(chuàng)建與這些語言兼容的匯編程序。操作系統(tǒng)選項:指定程序運行在什么操作系統(tǒng)之下。可選的只有OS_DOS或OS_OS2,默認(rèn)項是OS_DOS。

堆棧選項:有NEARSTACK和FARSTACK兩種。

NEARSTACK表示堆棧段寄存器SS和數(shù)據(jù)段寄存器DS指向同一個段,也就是堆棧段和數(shù)據(jù)段合并為一個段。

FARSTACK表示堆棧段和數(shù)據(jù)段不合并。

當(dāng)內(nèi)存模式為TINY、SMALL、MEDIUM和FLAT時,這個選項默認(rèn)為NEARSTACK,其他內(nèi)存模式時默認(rèn)為FARSTACK。882.近數(shù)據(jù)段定義偽指令 .DATA.DATA? .CONST.DATA創(chuàng)建一個數(shù)據(jù)段,段名是_DATA,主要定義有初值的變量。.DATA?用來定義沒有初值的變量,段名為_BSS。定義在.DATA?段的無初值變量不占用可執(zhí)行文件的空間。.CONST定義只讀數(shù)據(jù)段,段名是_CONST。

一個源文件中可以包含多個由.DATA、.DATA?和.CONST定義的數(shù)據(jù)段,TASM自動將其合并為一個物理段,使它們具有相同的段基址。893.遠(yuǎn)數(shù)據(jù)段定義偽指令

.FARDATA[段名]和.FARDATA?[段名]

它們分別用來定義已有初值的數(shù)據(jù)段和沒有初值的數(shù)據(jù)段。如果段名缺省,默認(rèn)段名分別為FAR_DATA和FAR_BSS。通過指定段名,可以在源文件中定義多個獨立的數(shù)據(jù)段。遠(yuǎn)數(shù)據(jù)段不會被合并。

4.代碼段定義偽指令 .CODE [段名]

如果內(nèi)存模式為SMALL,.CODE偽指令不需要給出“段名”,編譯器生成一個名為_TEXT的代碼段,對于多個代碼段的內(nèi)存模式,則應(yīng)該指明段名。

905.堆棧段定義偽指令 .STACK [堆棧大小]定義一個堆棧段,缺省大小為1024字節(jié),段名為STACK。6.TASM預(yù)定義符號 @CODE和@DATA@CODE表示由.CODE定義的代碼段的段名,在指令中表示段基址。@DATA表示由.DATA、.DATA?和.CONST定義的數(shù)據(jù)段的段名,在指令中表示段基址。注意:使用簡化段定義時,沒有段結(jié)束偽指令。一個新的段開始,就意味著上一個段到此結(jié)束。

91.MODEL MEDIUM ;內(nèi)存模式為中型.STACK 100H.DATAMSG1 DB "FIRSTMESSAGE”,0DH,0AH,"$".DATA? ;定義第二個數(shù)據(jù)段,未初始化X DB 10DUP(?).CODEMAIN PROC MOV AX,@DATA MOV DS,AX LEA DX,MSG1 CALL FARPTRDISP ;遠(yuǎn)調(diào)用。

MOV AX,4C00H INT 21HMAIN ENDP[例5.6] 本例中有兩個代碼段。92.CODE OTHER ;再定義一個代碼段DISPPROCFAR MOV AH,9 INT 21H RETDISP ENDP ENDMAIN ;源程序結(jié)束,入口地址MAIN93經(jīng)過鏈接,從對應(yīng)的MAP文件中可以看到程序有兩個代碼段、兩個數(shù)據(jù)段和一個堆棧段:Start StopLength Name

Class00000H00011H00012HMULTCODE_TEXTCODE00012H00016H00005HOTHER CODE00020H0002FH00010H_DATA DATA00030H0012FH00100HSTACK STACK00130H00130H00001H_BSS BSS

用TD觀察這個程序,發(fā)現(xiàn)_DATA和_BSS段使用相同的段基址,被合并為一個物理段。945.4.3 創(chuàng)建多模塊程序

模塊

:由不同的程序員編寫,以不同的文件名存放在磁盤中,這樣的源程序文件稱為“模塊”

模塊內(nèi)部由若干個段的定義組成,其中僅有一個模塊包含有主程序,其它的模塊包含了一些子程序和數(shù)據(jù)段、堆棧段等。

幾個源程序文件在連接、合并成為一個完整的可執(zhí)行程序時需要解決兩個問題:

不同源程序文件中的段如何排列、組合?一個源程序文件怎樣調(diào)用/訪問位于另一個文件中的子程序、變量?

95

為使模塊A中的程序能夠調(diào)用位于模塊B中的子程序,或者,訪問模塊B中的某個變量,需要在模塊A中用EXTERN偽指令申明要調(diào)用的過程名或變量名。 反之,為了使模塊B中的子程序、變量能夠被其它模塊所引用,需要在模塊B中用PUBLIC偽指令申明哪些子程序名或變量名是可以供其他模塊調(diào)用的。

96兩條偽指令的格式如下:EXTRN 符號名:TYPE[,……]PUBLIC 符號名1,符號名2,…

偽指令EXTRN中,符號名是需要引用的其它模塊中的子程序名、標(biāo)號或變量名。如果符號名是變量,則TYPE(類型)應(yīng)該是BYTE、WORD、DWORD等;如果符號名是標(biāo)號或過程名,則TYPE為NEAR或FAR。

偽指令PUBLIC說明本模塊中哪些子程序名、標(biāo)號、變量名可以被其它模塊引用,這些符號的類型在模塊內(nèi)部定義,PUBLIC偽指令中不需要再次指出符號的類型。

97[例5.7] 計算ARRAY數(shù)組元素之和,存放到SUM單元。;MODULA.ASM ;模塊A源程序EXTRNARRAYSUM:FAR ;聲明外部函數(shù)ARRAYSUM ;此函數(shù)將在本模塊中被調(diào)用PUBLICARRAY ;聲明全局變量,可供其他模塊使用DSEG1 SEGMENTPUBLIC ;數(shù)據(jù)段與其它模塊同名段合并成一個段

ARRAY DW 2,4,6,8 LENTH EQU $-ARRAYDSEG1 ENDSDSEG2 SEGMENT SUM DW ?DSEG2 ENDS98CSEG SEGMENT ASSUME CS:CSEG,DS:DSEG2 MAIN PROC FAR MOV AX,DSEG2 MOV DS,AX MOV CX,LENTH CALL ARRAYSUM ;調(diào)用外部函數(shù)

MOV SUM,AX MOV AX,4C00H INT 21H MAIN ENDPCSEG ENDS END MAIN99;MODULB.ASM ;模塊B源程序EXTRNARRAY:WORD ;聲明外部變量PUBLICARRAYSUM

;該模塊沒有數(shù)據(jù)段STAK SEGMENT 'STACK' DW 10DUP(?)STAK ENDS

100CSEG SEGMENT ASSUME CS:CSEGARRAYSUM PROC FAR PUSH DS ;保留原DS MOV AX,SEGARRAY;把外部變量的段基址放入

MOV DS,AX ;段寄存器DS PUSH SI MOV AX,0 LEA SI,ARRAYL1: ADD AX,[SI] ADD SI,2 LOOP L1 POP SI POP DS ;恢復(fù)原DS RET ARRAYSUM ENDPCSEG ENDS END ;模塊B不需要填寫“入口地址”101本例中,為了使模塊B能夠訪問模塊A中定義的數(shù)組ARRAY,采取了兩條措施:

通過EXTRN偽指令將ARRAY定義為外部字變量;把ARRAY所在段的段基址裝入DS寄存器;

也可以在模塊B中定義一個與模塊A中數(shù)據(jù)段DSEG1同名的“空”數(shù)據(jù)段,通過段的PUBLIC屬性將它們“合并”為一個段。這樣,模塊B就可以像訪問自己定義的變量一樣訪問ARRAY數(shù)組了。

102;MODULB.ASM ;

模塊BPUBLICARRAYSUM ;聲明子程序ARRAYSUM可 ;

供其它模塊調(diào)用EXTRNARRAY:WORD ;聲明ARRAY是外部字變量DSEG1 SEGMENTPUBLIC ;為了使模塊B能訪問

;模塊A中的變量 DSEG1 ENDS ;本數(shù)據(jù)段與模塊A的同名

;

數(shù)據(jù)段合并成一個段

STAK SEGMENT 'STACK’;堆棧段

DW 10DUP(?)STAK ENDSMODELB.ASM可以修改為:103CSEG SEGMENT ;代碼段ASSUME CS:CSEG,DS:DSEGARRAYSUM PROC FAR ;求和子程序

PUSH DS MOV AX,DSEG1 MOV DS,AX PUSH SI MOV AX,0 LEA SI,ARRAY ;引用了外部變量ARRAYL1: ADD AX,[SI] ADD SI,2 ;AX返回和

LOOP L1 POP SI POP DS RET ARRAYSUM ENDPCSEG ENDS END1045.5匯編語言與C語言混合編程5.5.1C語言源程序編譯為匯編源程序5.5.2C語言調(diào)用匯編子程序5.5.3 匯編語言調(diào)用C語言函數(shù)105

與高級語言開發(fā)的程序相比,匯編語言開發(fā)的應(yīng)用程序占用內(nèi)存空間小、運行速度快、能夠直接控制硬件。 但是,用匯編語言開發(fā)的程序依賴于具體CPU的指令系統(tǒng),可移植性差,用它來編寫大規(guī)模的程序比使用高級語言耗時多。 所以,在一些對空間、速度要求較高的應(yīng)用場合,可以用高級語言編寫操作界面、人機(jī)接口等“外層”程序,用匯編語言編寫需要進(jìn)行大量計算的“內(nèi)核”部分,充分發(fā)揮兩類語言各自的優(yōu)勢,這樣的編程方法稱為混合編程。1065.5.1C語言源程序編譯為匯編源程序

高級語言編寫的程序在執(zhí)行之前,必須通過“編譯”轉(zhuǎn)換為由機(jī)器指令構(gòu)成的“可執(zhí)行程序”,程序中的每一條語句被轉(zhuǎn)換為幾條機(jī)器指令。107#include<stdio.h>intmax(int,int) /*使用之前,先申明函數(shù)max原型*/main(){intc;c=max(-5,9);

printf("maxis%d",c);}intmax(intx,inty){

intz;z=x>y?x:y;return(z);}[例5-8]程序Simple.c求兩個數(shù)中最大值并輸出。108主函數(shù)main()做了三件事:

調(diào)用函數(shù)max(intx,inty),得到兩個數(shù)的最大值;

把這個最大值賦給變量c;

調(diào)用printf()函數(shù)輸出c的值。109在BorlandC中鍵盤輸入編譯命令:

BCC–Ssimple.c或者在TurboC中,退出集成環(huán)境,鍵盤輸入編譯命令:

TCC–ssimple.c將這個C語言程序編譯為匯編語言程序simple.asm110_TEXTSEGMENTBYTEPUBLIC'CODE' ;代碼段從此開始;MAIN() ASSUME CS:_TEXT_MAIN PROC NEAR ;主程序

PUSH BP ;保存BP MOV BP,SP ;BP記錄進(jìn)入之后的堆棧位置

SUB SP,2 ;為整型變量C留出2字節(jié)存儲單元主函數(shù)main()對應(yīng)的匯編源程序:111; {INTC

;C=MAX(-5,9); MOV AX,9 PUSH AX ;參數(shù)入棧,右側(cè)參數(shù)(9)先入棧

MOV AX,-5 PUSH AX ;參數(shù)入棧,左側(cè)參數(shù)(-5)后入棧

CALL NEARPTR_MAX

;調(diào)用過程,過程名前加下劃線“_” POP CX POP CX ;廢棄棧頂?shù)膬蓚€字(-5和9)

MOV WORDPTR[BP-2],AX

;把AX中的返回結(jié)果送入變量c112; PRINTF("MAXIS%D",C);

;調(diào)用PRINTF前設(shè)置入口參數(shù)

PUSH WORDPTR[BP-2] ;變量C的值壓入堆棧

MOV AX,OFFSETDGROUP:S@ PUSH AX ;打印格式字符串首地址壓入堆棧

CALL NEARPTR_PRINTF POP CX POP CX ;廢棄棧頂?shù)膬蓚€字(C和格式串首地址)

; } MOV SP,BP ;恢復(fù)SP POP BP ;恢復(fù)BP RET _MAIN ENDP113把調(diào)用該函數(shù)使用的實參按照從右到左的順序壓入堆棧。如果實參是變量(表達(dá)式),把它的值壓入堆棧。如果實參是字符串,把字符串的首地址壓入堆棧;調(diào)用該函數(shù);3. 從函數(shù)返回后,執(zhí)行POP指令廢棄堆棧中的實參。C語言調(diào)用一個函數(shù)的過程是:114

z←SPbp←BPmain的返回地址

-5

9

返回115; INTMAX(INTX,INTY);C程序中的MAX過程,對應(yīng)下面的匯編語句

ASSUME CS:_TEXT_MAX PROC NEAR PUSH BP ;保存BP MOV BP,SP ;BP記錄進(jìn)入之后的堆棧位置

SUB SP,2 ;為整型變量Z留出2字節(jié)存儲單元

MOV DX,WORDPTR[BP+4] ;-5送DX MOV BX,WORDPTR[BP+6] ;9送BX函數(shù)max(intx,inty)對應(yīng)的匯編語言源程序:116; { ; INTZ; ; Z=X>Y?X:Y; CMP DX,BX JLE SHORT@1@86 MOV AX,DX JMP SHORT@1@114@1@86:MOV AX,BX@1@114:MOV WORDPTR[BP-2],AX;最大值置入Z

117

;RETURN(Z); MOV AX,WORDPTR[BP-2] ;變量Z的值送AX JMP SHORT@1@142@1@142:; }

MOV SP,BP ;恢復(fù)SP

POP BP ;恢復(fù)BP

RET ;返回

_MAX ENDP

···

_TEXT ENDS118_DATA SEGMENTWORDPUBLIC'DATA'S@ LABEL BYTE DB 'maxis%d' ;printf函數(shù)使用的格式字符串

DB 0 ;用0表示字符串結(jié)束_DATA ENDS_TEXT SEGMENTBYTEPUBLIC'CODE'_TEXT ENDS PUBLIC _MAIN PUBLIC _MAX EXTRN _PRINTF:NEAR;聲明使用外部函數(shù)printf_S@ EQU S@ END119BP壓入堆棧,把此刻堆棧頂位置SP的值送BP保存;修改SP的值,在堆棧中為動態(tài)變量分配存儲單元;執(zhí)行函數(shù)內(nèi)的各項操作;對于int函數(shù),運行結(jié)果置入AX,對于long函數(shù),運行結(jié)果置入DX、AX;恢復(fù)SP和BP的值,用RET指令返回主調(diào)函數(shù);

一個C語言函數(shù)的執(zhí)行過程是:

1205.5.2 C語言程序調(diào)用匯編子程序

用高級語言編寫主程序,用匯編語言編寫子程序。將它們編寫完成后,分別編譯/匯編成為“目標(biāo)文件”(XXXX.obj),然后把它們連接起來,形成可執(zhí)行文件(XXXX.exe)。

C語言提供了和匯編語言一樣的6種存儲模型:微型(tiny)、小型(small)、中型(medium)、緊湊(compact)、大型(large)和巨型(huge)。

在Borlandc集成環(huán)境中,在菜單條上先后選擇Option/Compiler/CodeGeneration,出現(xiàn)名為“CodeGeneration”的選擇窗口,在Model選項中選擇所需要的存儲模型。

C語言程序和匯編語言程序必須使用相同的存儲模型。

121122extern intmax(int,int); /*聲明外部函數(shù)*/main() /*C語言主程序*/{ printf(“maxis%d”,max(-5,9));}[例5.9] 輸出兩數(shù)中較大者(1)C語言程序ex509.c,存儲模型為small。123.MODEL SMALLPUBLIC _MAX ;聲明此函數(shù)可供外部模塊調(diào)用,

;名字前加下劃線.CODE_MAX PROC PUSH BP MOV BP,SP ;建立堆棧指針

MOV AX,[BP+4] ;

取第一個參數(shù)-5送AX CMP AX,[BP+6] ;

與第二個參數(shù)9比較

JG EXIT ;如果第一參數(shù)較大,直接返回

MOV AX,[BP+6] ;第二參數(shù)較大,取到AX中EXIT: POP BP RET ;整型函數(shù)通過AX返回函數(shù)值_MAX ENDP END(2)匯編語言程序ex509.asm124

bp←BP,SPmain的返回地址

-5

9

進(jìn)入子程序_max后的堆棧

返回1251)

編寫匯編子程序文件ex509.asm。

2)

進(jìn)入BC環(huán)境,選擇Project/OpenProject,新建一

個工程文件ex509.prj,可以建在與C程序和匯編

子程序同一個目錄下。

3)

選擇Additem,將文件名ex509.asm和ex509.c加入

到工程文件中。

4)

選擇Compile/Buildall,編譯、連接該工程文件,

生成ex509.exe。在Borlandc集成環(huán)境中編譯、連接過程如下:1265.5.3匯編語言程序調(diào)用C語言函數(shù)[例5-10]匯編語言程序調(diào)用C語言函數(shù)計算Y=XN

函數(shù)原型:

voidpower(int*y,intx,intn)

功能:計算Y=XN

入口:*Y(指針),X,N

出口:無(XN的值已通過指針存入Y)127INCLUDE YLIB.HEXTRN_POWER:NEAR ;聲明外部函數(shù),函數(shù)名用下劃線“_”開始.MODEL SMALL.DATA X DW 4N DW 3Y DW ?MESS DB 0DH,0AH,“Power=$”(1)匯編語言編寫的主程序EX510.ASM

通過調(diào)用c函數(shù)計算Y=XN128.CODE START: MOV AX,@DATA MOV DS,AX MOV AX,N PUSH AX ;N,X的值入棧,傳遞給C子程序

MOV AX,X PUSH AX

LEA AX,Y ;Y的地址入棧,傳遞給C子程序

PUSH AX CALL _POWER ;調(diào)用子程序

SUB SP,6 ;廢棄堆棧中的入口參數(shù)

LEA DX,MESS MOV AX,Y CALL WRITEDEC ;輸出結(jié)果

CALL CRLF MOV AX,4C00H INT 21H END START129/*power.c

求冪子程序*/voidpower(int*y,intx,intn){ int p,i; if(n<=1)p=1; else for(p=1,i=1;i<=n;i++)p=p*x; *y=p;}(2)C語言編寫的函數(shù)(子程序),用來求XN1301)

利用BC集成環(huán)境,對power.c進(jìn)行編譯:按下 ALT+F9,生成power.obj。2)

對匯編語言程序ex510.asm進(jìn)行匯編,生成 ex510.obj文件。3)用TLINK命令鏈接:tlinkex510+power,,,ylib16.lib 生成與第一個文件同名的ex510.exe文件。4)

執(zhí)行ex510.exe,在屏幕上輸出:

Power=64 (3)編譯、連接、運行1315.6 DOS和BIOS調(diào)用

5.6.1BIOS功能調(diào)用5.6.2DOS功能調(diào)用132

在PC機(jī)主板的只讀存儲器芯片(ROM)中,有一組特殊的程序,稱為基本的輸入輸出系統(tǒng)(簡稱BIOS)。BIOS由許多子程序組成,這些子程序為應(yīng)用程序提供了一個使用IBM-PC系統(tǒng)中常用設(shè)備的接口。

MS-DOS操作系統(tǒng)在更高一個層次為用戶提供了與系統(tǒng)及硬件的接口,稱為“DOS功能調(diào)用”。許多DOS功能調(diào)用實現(xiàn)時需要調(diào)用BIOS提供的相關(guān)功能。1335.6.1 BIOS功能調(diào)用

BIOS功能調(diào)用通過軟中斷指令I(lǐng)NT實現(xiàn),格式如下:

INT n n的取值范圍0~255,每個n對應(yīng)一段子程序。 與一般子程序調(diào)用一樣,在BIOS功能調(diào)用前也要設(shè)置入口參數(shù),功能調(diào)用也會產(chǎn)生返回參數(shù)(不是所有的功能都有參數(shù)返回)。134AH=0:從鍵盤讀入一鍵(等待輸入)

返回參數(shù):AL=ASCII碼,AH=掃描碼。

功能:從鍵盤讀入一個鍵后返回,按鍵不顯示在屏幕上

對于無相應(yīng)ASCII碼的鍵,如功能鍵等,AL返回0。此時,應(yīng)再次以“

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論