?? 匯010.txt
字號(hào):
了解的內(nèi)容:打印機(jī)輸出、磁盤輸入輸出、通信口輸入輸出和異常情況處理等程序的編程方法,程序段前綴PSP的含義及其應(yīng)用。
掌握的內(nèi)容:駐留程序的設(shè)計(jì)方法,結(jié)構(gòu)和記錄在匯編語言中的應(yīng)用方法,文件操作的編程,鼠標(biāo)(Mouse)應(yīng)用程序的編程。
熟練掌握的內(nèi)容:處理鍵盤輸入的各種方法,屏幕的定位和顯示方法,字符串的處理方法,二進(jìn)制數(shù)據(jù)和字符串之間的轉(zhuǎn)換方法。
建議學(xué)習(xí)時(shí)間:12小時(shí)。
第10章 應(yīng)用程序的設(shè)計(jì)
在前面各章節(jié)中,我們側(cè)重介紹了匯編語言程序設(shè)計(jì)中各組成部分的作用,本章的重點(diǎn)是對(duì)前面所學(xué)知識(shí)的綜合運(yùn)用。希望通過各種不同類型的例子,使讀者能夠掌握用匯編語言編程的基本技巧。
10.1 字符串的處理程序
字符或字符串是一類重要的非數(shù)值計(jì)算的處理對(duì)象。許多編輯軟件都具有字符串查找、替換、大小寫的轉(zhuǎn)換、單詞的自動(dòng)識(shí)別等功能,網(wǎng)絡(luò)上的信息搜索也是現(xiàn)在一種常用的功能等,這些功能的實(shí)現(xiàn)無疑都要涉及到字符串的處理功能。
為了方便對(duì)字符串的處理,各種常用的編程環(huán)境也都給予了足夠的支持。如:C語言編程環(huán)境提供了大量處理字符串的標(biāo)準(zhǔn)函數(shù),象strlen、strcmp和strcpy等函數(shù);C++、VC或VB等編程環(huán)境提供了字符串類String等。這些函數(shù)或類大大方便了程序員的編程。
在計(jì)算機(jī)系統(tǒng)內(nèi),為了加快字符串的處理,在其指令系統(tǒng)中設(shè)置了多條處理字符串的指令,其詳細(xì)內(nèi)容請(qǐng)參閱第5.2.11節(jié)中的介紹。
下面我們將通過幾個(gè)例子來學(xué)習(xí)匯編語言處理字符串的方法。
例10.1 編寫一個(gè)求字符串長(zhǎng)度的子程序Strlen,要求字符串的首地址為入口參數(shù),且以ASCII碼0為結(jié)束符,CX為出口參數(shù),其存放該字符串的長(zhǎng)度。
解:
.MODEL SMALL, C
.DATA
buff DB "This is a example.", 0
.CODE
Strlen PROC USES AX BX, String:PTR BYTE
MOV BX, String
XOR CX, CX
MOV AL, [BX]
.WHILE AL!=0
INC
CX
INC
BX
MOV
AL, [BX]
.ENDW
RET
Strlen ENDP
.STARTUP
INVOKE Strlen, ADDR buff
.EXIT 0
END
例10.2 編寫一個(gè)把字符串中的所有小寫字符轉(zhuǎn)換成大寫字符的子程序Strupr,要求字符串的首地址和結(jié)束符為其入口參數(shù)。
解:
.MODEL SMALL, C
.DATA
buff
DB "This is a example.", 0
.CODE
Strupr PROC USES AX BX, String:PTR BYTE, Tail:BYTE
MOV BX, String
.REPEAT
MOV
AL, [BX]
.IF AL>='a' && AL<='z'
SUB
AL, 20H
MOV
[BX], AL
.ENDIF
INC
BX
.UNTIL AL==Tail
RET
Strupr ENDP
.STARTUP
INVOKE Strupr, ADDR buff, 0
.EXIT 0
END
例10.3 編寫一個(gè)從字符串中拷貝子串的子程序Strncpy,它有四個(gè)參數(shù)str1、str2、idx和num,其具體功能為把字符串str2中從第idx個(gè)(從0開始記數(shù))字符開始、num個(gè)字符傳送給str1,字符串str1和str2都是以ASCII碼0為結(jié)束符。
解:
.MODEL SMALL, C
.DATA
str1
DB "12345ABCDEF", 0
str2
DB 20 DUP('A')
.CODE
Strlen PROC USES AX BX, String:PTR BYTE
…… ;參見例10.1
Strlen ENDP
Strncpy PROC USES AX CX DI SI DS ES, str1:FAR PTR BYTE, str2:FAR PTR BYTE, idx:WORD, num:WORD
LES DI, str1
LDS SI, str2 ;取兩個(gè)字符串的首地址
INVOKE Strlen, SI ;計(jì)算源字符串的長(zhǎng)度,在CX中
MOV AX, idx
.IF AX >= CX ;若字符起點(diǎn)就超過源串的長(zhǎng)度
MOV
BYTE PTR ES:[DI], 0 ;拷貝的字符串為“空”
JMP
over
.ENDIF
ADD SI, AX ;定源串中字符的起點(diǎn)SI
MOV CX, num
CLD
.REPEAT
LODSB
STOSB
.UNTILCXZ AL==0
.IF AL!=0 ;設(shè)置目標(biāo)串的結(jié)束符
MOV
BYTE PTR[DI], 0
.ENDIF
over: RET
Strncpy ENDP
.STARTUP
INVOKE Strncpy, ADDR str2, ADDR str1, 3, 5
.EXIT 0
END
例10.4 編寫一個(gè)把字符串中空格和TAB壓縮掉的子程序Compress,字符串String是以ASCII碼0為結(jié)束符。
解:
.MODEL SMALL, C
.DATA
SPACE
EQU 20H
TAB
EQU 9H
Buff
DB "12 3 4 Ab cdef", 0
.CODE
Compress PROC USES AX BX SI DS, String:FAR PTR BYTE
LDS SI, String ;SI用于掃描字符串的指針
MOV BX, SI ;BX用于存放結(jié)果的指針
.REPEAT
MOV
AL, [SI]
INC
SI
.IF AL!=SPACE && AL!=TAB
MOV [BX], AL
INC BX
.ENDIF
.UNTIL AL==0
RET
Compress ENDP
.STARTUP
INVOKE Compress, ADDR Buff
.EXIT 0
END
從上面四個(gè)例子,我們不難看出處理字符串的一般方法,感興趣的讀者可自行編寫實(shí)現(xiàn)字符串變小寫、整體拷貝、逆轉(zhuǎn)和查找等功能的子程序,甚至還可以建立起自己的字符串處理庫文件。
10.2 數(shù)據(jù)的分類統(tǒng)計(jì)程序
數(shù)據(jù)的分類和統(tǒng)計(jì)也是一類非數(shù)值計(jì)算,數(shù)據(jù)的分類統(tǒng)計(jì)方法在例6.10中已介紹,下面通過一個(gè)例子介紹數(shù)據(jù)的分類存儲(chǔ)問題。
例10.5 統(tǒng)計(jì)從地址0040H:0000H開始的100個(gè)字中,把正數(shù)和負(fù)數(shù)按照它們先后出現(xiàn)的次序分別存儲(chǔ)在緩沖區(qū)Data1和Data2,并把每類的個(gè)數(shù)存入相應(yīng)緩沖區(qū)的第一個(gè)字單元中。
解:由于在指定地址之后的100個(gè)字中,可能存在全是正數(shù)或負(fù)數(shù)的情況,所以,緩沖區(qū)Data1和Data2的容量都應(yīng)是100個(gè)字。
.MODEL SMALL
.DATA
Num = 100
Data1
DW ?, Num dup(?)
Data2
DW ?, Num dup(?)
.CODE
.STARTUP
MOV
AX, 40H
MOV
ES, AX
LEA
SI, Data1+2 ;指向存儲(chǔ)正數(shù)的緩沖區(qū)
LEA
DI, Data2+2 ;指向存儲(chǔ)負(fù)數(shù)的緩沖區(qū)
XOR
BX, BX ;BX用于掃描存儲(chǔ)單元
MOV
CX, 100 ;字符個(gè)數(shù)
.REPEAT
MOV
AX, ES:[BX]
ADD
BX, 2
CMP
AX, 0
.CONTINUE .IF ZERO?
JL
next1
MOV
[SI], AX ;向正數(shù)緩沖區(qū)內(nèi)存儲(chǔ)數(shù)據(jù)
ADD
SI, 2
.CONTINUE
next1:
MOV
[DI], AX ;向負(fù)數(shù)緩沖區(qū)內(nèi)存儲(chǔ)數(shù)據(jù)
ADD
DI, 2
.UNTILCXZ
SUB
SI, OFFSET Data1+2
SUB
DI, OFFSET Data2+2
SHR
SI, 1
SHR
DI, 1
MOV
Data1, SI
MOV
Data2, DI ;把每類的統(tǒng)計(jì)個(gè)數(shù)存入緩沖區(qū)的第一個(gè)字單元
.EXIT
0
END
例10.6 用鍵盤輸入任意一字符串,分類統(tǒng)計(jì)該字符串中每個(gè)數(shù)字和字母的出現(xiàn)次數(shù)。
解:
.MODEL SMALL
.DATA
N = 80
Buff
DB N, ?, N DUP(?)
Num
DW 36 DUP(0) ;每個(gè)字用于存放'0'~'9','A'~'Z'出現(xiàn)的個(gè)數(shù)
.CODE
.STARTUP
LEA
DX, Buff
MOV
AH, 0AH
INT
21H ;輸入一個(gè)字符串
XOR
CH, CH
MOV
CL, Buff+1 ;CX=輸入字符串的個(gè)數(shù)
LEA
SI, Buff+2
XOR
BX, BX
.REPEAT
MOV
BL, [SI] ;考慮下面的思考題
INC
SI
.IF BL>='0' && BL<='9'
;分類統(tǒng)計(jì)'0'~'9'中的每個(gè)數(shù)字的個(gè)數(shù)
SUB
BL, '0'
ADD
BX, BX
INC
Num[BX]
.CONTINUE
.ENDIF
.IF BL>='a' && BL<='z'
SUB
BL, 20H ;小寫變大寫
.ENDIF
.IF BL>='A' && BL<='Z'
;分類統(tǒng)計(jì)'A'~'Z'中的每個(gè)字母的個(gè)數(shù)
SUB
BL, 'A'-10
ADD
BX, BX
INC
Num[BX]
.ENDIF
.UNTILCXZ
.EXIT 0
END
思考題:在本例中,用指令“MOV BL, [SI]”來把當(dāng)前檢測(cè)的字符存入BL,當(dāng)然,我們也可以用AL來代替BL,有關(guān)指令要作相應(yīng)的改動(dòng),但這樣做,會(huì)更方便嗎?希望讀者能知道:為什么要用BL,而不用AL?
10.3 數(shù)據(jù)轉(zhuǎn)換程序
數(shù)據(jù)類型轉(zhuǎn)換是輸入輸出過程中經(jīng)常遇到的問題。輸入時(shí),計(jì)算機(jī)系統(tǒng)要把用戶從鍵盤上輸入的字符串轉(zhuǎn)變成相應(yīng)的數(shù)值,并存儲(chǔ)在內(nèi)存中;輸出時(shí),要把計(jì)算機(jī)內(nèi)部的二進(jìn)制數(shù)據(jù)形式轉(zhuǎn)換成相應(yīng)的十進(jìn)制字符串,然后再輸出。
在高級(jí)語言編程環(huán)境中,程序員能用各種輸入輸出語句,按一定的格式進(jìn)行交互式操作,很少或根本不關(guān)心輸入輸出是如何實(shí)現(xiàn)的。有的程序員甚至認(rèn)為其輸入的就是十進(jìn)制數(shù)值,輸出數(shù)據(jù)也就是把內(nèi)存中存儲(chǔ)的數(shù)據(jù)直接輸出出來。其實(shí),輸入輸出過程并不是如此簡(jiǎn)單,計(jì)算機(jī)系統(tǒng)要進(jìn)行復(fù)雜而又細(xì)致的數(shù)據(jù)類型轉(zhuǎn)換和格式化等工作。
本節(jié)試圖通過用匯編語言實(shí)現(xiàn)數(shù)據(jù)類型的轉(zhuǎn)換來反映輸入輸出的本來面目,使程序員在用高級(jí)語言編程時(shí),對(duì)其輸入輸出語句的實(shí)現(xiàn)過程有所了解,也知道有別人(或編譯程序)幫他完成了輸入輸出的準(zhǔn)備工作。
例10.7 編寫一個(gè)程序,它能把字類型變量的數(shù)值以十進(jìn)制形式輸出出來。若該數(shù)值為負(fù)數(shù),則需要輸出負(fù)號(hào)"-",否則,不輸出符號(hào)。
解:
鑒于按二進(jìn)制輸出的特殊性,我們可以把它優(yōu)化成例10.8的形式,按十六進(jìn)制輸出也可以按“四位二進(jìn)制對(duì)應(yīng)一位十六進(jìn)制”的規(guī)則進(jìn)行優(yōu)化的。
例10.7是用“用16位除10”的方法從低向高依次得到每位的數(shù)值,但若待輸出的數(shù)據(jù)是32位,用10除之后,其商很可能會(huì)超過16位,所以,不能簡(jiǎn)單地引用例10.7的方法來輸出32位二進(jìn)制。
假設(shè):32位二進(jìn)制數(shù)Z為A×216+B,其中:A和B都是16位二進(jìn)制數(shù)。 用10去除A,得:A=A1×10+A2,于是,
(1)
假設(shè)A2×216+B被10除后所得的商和余數(shù)分別為B1和C1(B1≥0,C1≥0)。
利用式(1)和“A2<10”,我們不難看出:Z的個(gè)位就是C1和B1<216。
令Z1=A1×216+B1,顯然,Z1就是Z/10所得到的商。
對(duì)于Z1,再利用式(1)得到商Z2和C2。……,重復(fù)上面的步驟,直到所得商為0為止。
下面的例10.9就是利用上面方法來輸出32位二進(jìn)制數(shù)值。
例10.9 編寫一個(gè)子程序,該子程序能把32位二進(jìn)制變量的數(shù)值以十進(jìn)制形式輸出出來。若該數(shù)值為負(fù)數(shù),則需要輸出負(fù)號(hào)"-",否則,不輸出符號(hào)。
解:
.MODEL SMALL, C
.DATA
CR = 13
LF = 10
Data1
DD 908976789
.CODE
;子程序Display是按十進(jìn)制輸出32位二進(jìn)制數(shù)值SOURCE
Display PROC USES AX BX CX DX SI DI SOURCE:DWORD
LOCAL FLAG:BYTE ;定義一個(gè)字節(jié)類型的局部變量FLAG
MOV BX, WORD PTR [SOURCE]
MOV CX, WORD PTR [SOURCE+2]
MOV FLAG, 0 ;FLAG=0——正數(shù)
CMP CX, 0
JGE next
INC FLAG ;FLAG=1——負(fù)數(shù)
NOT BX
NOT CX
ADD BX, 1 ;能否用指令I(lǐng)NC BX?
ADC CX, 0 ;上四條指令把32位數(shù)CX-BX變?yōu)檎龜?shù)
next:
XOR DI, DI ;壓入堆棧字符的個(gè)數(shù)
MOV SI,10 ;用10來除
.REPEAT ;本循環(huán)把32位二進(jìn)制數(shù)轉(zhuǎn)換成十進(jìn)制
XOR
DX, DX ;數(shù)的字符串存入堆棧之中
MOV
AX, CX
DIV
SI
MOV
CX, AX
MOV
AX, BX
DIV
SI
ADD
DL, '0'
PUSH
DX
INC
DI
MOV
BX, AX
.UNTIL BX==0 && CX==0
.IF FLAG==1 ;判斷前面轉(zhuǎn)換的數(shù)是否為負(fù)數(shù)
MOV
AL, '-' ;若是,把符號(hào)'-'壓入堆棧
PUSH
AX
INC
DI
.ENDIF
MOV CX, DI
.REPEAT ;本循環(huán)把堆棧中的字符串顯示出來
POP
DX
MOV
AH, 2
INT
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -