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