實驗6 實踐課程中的程序
(1)將課程中所有講解過的程序上機測試,用debug跟蹤其執行過程,並在過程中理解所講內容。
問題7.6 將data段中每個單詞的頭一個字母改為大寫字母。
assume cs:code
data segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
data ends
code segment
程序分析:
1)數據段定義了6個字符串結構,長度一致都是16字節,並且字符都是連續的,並且頭一個字母在字符串中的位置都是第4個字符,在上面的尋址方式中,我們發現[bx+idata]方式比較適合這種情況,idata代表每個字符串起始地址,bx代表基本偏移地址,bx的偏移量也是有規律的16。
2)在debug中,這6個字符串顯示的正好是6行16列的字符,是一個我們在C語言中講到的二維數組[6][16],其實它在內存中是線性單列排列的。
3)遇到此種情況我們使用[bx+idata]的方式進行內存的尋址比較科學。
完整的代碼如下:
assume cs:code
data segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;將ds:bx指向data第一個單元
mov cx, 6 ;需要修改6次
s: mov al, [bx+3] ;使用[bx+idata]尋址方式,並送入al(單字節)
and al, 11011111b ;轉換為大寫字母
mov [bx+3], al ;回寫內存
add bx, 16 ;6個字符串長度一致,都是16字節,增量為16
loop s
mov ax, 4c00H
int 21H
code ends
end start
結果分析:
(1)在循環中,如遇到loop指令,首先我們要認識到,(cx)=(cx)-1;直到cx=0退出循環。
問題7.7將data段中每個單詞的字母改為大寫字母。
assume cs:code
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
程序分析:
(1)數據段定義了4個字符串,長度依然是16字節,並且是連續存儲的,這次是要求將每個字符串的小寫字母都給修改了,這樣[bx+idata]就不合適了,我們采用[bx+si]方式尋址。bx依然代表基本偏移地址,si代表了每個字符串每個字母的偏移地址。
(2)一個循環肯定滿足不了要求了,這里需要二重循環,外層循環遞增bx的值;內存循環對每個字符串的字符進行變換操作。
(3)對於loop循環來說,它判斷的是cx計數器,對於二重循環,一個cx值顯然滿足不了要求。書中代碼中的cx設置就有問題了。
問題7.8 解決cx計數器重復設置的問題:
程序分析:我們可以想法將外層循環計數器保存起來,待某一個內層循環執行完畢后,在恢復。這樣可以解決了cx計數器沖突的問題。
改進的代碼:
assume cs:code
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;bx作為基本偏址變量
mov cx, 4 ;需要外循環4次,修改4個字符串
s0: mov dx, cx ;將外層循環計數器cx保存到dx中
mov si, 0 ;si作為每個字符串的偏址
mov cx, 3 ;內存循環s1,需要循環3次
s1: mov al, [bx+si] ;使用[bx+si]尋址方式,並送入al(單字節)
and al, 11011111b ;轉換為大寫字母
mov [bx+si], al ;回寫內存
inc si
loop s1
add bx, 16 ;6個字符串長度一致,都是16字節,增量為16
mov cx, dx ;將外層循環計數器cx從dx中恢復
loop s0
mov ax, 4c00H
int 21H
code ends
end start
結果分析:
(1)我們通過將cx寄存器變量保存的方式,來解決多重循環cx變量沖突的問題。
(2)此例中我們解決方式是將cx保存到dx中,在8086CPU中,寄存器本來就比較緊張,段寄存器肯定不能用,ip是程序的指針、dx一般用於結果的輸出,占用后又出現新的問題,sp是默認的ss棧段的指針;顯然,將臨時的數據保存在一個寄存器中的方式是不合適的。
(3)既然不能講暫存的數據(它可能是寄存器變量的值,也可能是內存中單元的值)保存到一個寄存器中,那么使用內存作為暫存空間是可行的。我們將代碼繼續改進。
改進后的代碼:
assume cs:code
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
dw 0 ;定義一個字,用於暫存cx值
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;bx作為基本偏址變量
mov cx, 4 ;需要外循環4次,修改4個字符串
s0: mov ds:[40H], cx ;將外層循環計數器cx保存到data內存中
mov si, 0 ;si作為每個字符串的偏址
mov cx, 3 ;內存循環s1,需要循環3次
s1: mov al, [bx+si] ;使用[bx+si]尋址方式,並送入al(單字節)
and al, 11011111b ;轉換為大寫字母
mov [bx+si], al ;回寫內存
inc si
loop s1
add bx, 16 ;6個字符串長度一致,都是16字節,增量為16
mov cx, ds:[40H] ;將外層循環計數器cx從data內存中恢復
loop s0
mov ax, 4c00H
int 21H
code ends
end start
結果分析:
(1)在data段中定義了一個字單元空間,用於暫存cx的值。用直接尋址方式就行了。它在data段中偏移地址是40H,那么ds:[40H]==data:[40H]就代表了內存的那個字。
(2)這種情況,如果需要暫存的數據多的情況下,比較混亂,你還得記住內存單元的地址。
(3)一般來說,在需要暫存數據的時候,我們都應該使用棧。
繼續改進代碼,使用人工創建的棧空間來暫存cx值。
assume cs:code
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
stack segment
db 16 dup (0) ;人工定義一個棧段,空間16個字節,初始化為0
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;bx作為基本偏址變量
mov cx, 4 ;需要外循環4次,修改4個字符串
mov ax, stack
mov ss, ax ;人工創建一個棧結構
mov sp, 16 ;將ss:sp指向棧頂
s0: push cx ;將外層循環計數器cx保存到stack棧中
mov si, 0 ;si作為每個字符串的偏址
mov cx, 3 ;內存循環s1,需要循環3次
s1: mov al, [bx+si] ;使用[bx+si]尋址方式,並送入al(單字節)
and al, 11011111b ;轉換為大寫字母
mov [bx+si], al ;回寫內存
inc si
loop s1
add bx, 16 ;6個字符串長度一致,都是16字節,增量為16
pop cx ;將外層循環計數器cx從stack棧中恢復
loop s0
mov ax, 4c00H
int 21H
code ends
end start
結果分析:
(1)以后我們遇到越來越多的將暫存的數據存儲在棧空間中去。下面講到的子程序就大量使用棧。
(2)此例中我們使用的是人工創建的棧結構,還是就是系統自動創建的棧結構,那個我們不需要在內存中定義棧空間所需的單元。
(2)編程完成7.9問題中的程序
問題 7.9編程將data段中每個單詞的前4個字母改為大寫字母。
assume cs:code
data segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
data ends
stack segment
db 16 dup (0) ;人工定義一個棧段,空間16個字節,初始化為0
stack ends
程序分析:
(1)4個字符串,每個字符串長度都相同,都是16個字節,並且是連續的,此時我們可以把這種結構看成是一個二維數組。每個單詞字符長度都大於4.
(2)需要改變的是每個字符串的第4個字符開始的4個字符,那么在每一行,我們表示內存單元可以使用[si+idata]的方式尋址,idata代表每個行(每個字符串)開始地址3.si作為每行的偏移地址,偏移量從0~3;由於是個二維數組結構,bx代表每行的偏移量,偏移量從0~3,最終我們采用[bx+si+idata]尋址的方式來定位每行的字符串。
代碼如下:
assume cs:code
data segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
data ends
stack segment
db 16 dup (0) ;人工定義一個棧段,空間16個字節,初始化為0
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;bx作為基本偏址變量
mov ax, stack
mov ss, ax ;人工創建一個棧結構
mov sp, 16 ;將ss:sp指向棧頂
mov cx, 4 ;需要外循環4次,修改4個字符串
s0: push cx ;將外層循環計數器cx保存到stack棧中
mov si, 0 ;si作為每個字符串的偏址
mov cx, 4 ;內存循環s1,需要循環4次
s1: mov al, [bx+si+3] ;使用[bx+si+3]尋址方式,並送入al(單字節)
and al, 11011111b ;轉換為大寫字母
mov [bx+si+3], al ;回寫內存
inc si
loop s1
add bx, 16 ;4個字符串長度一致,都是16字節,增量為16
pop cx ;將外層循環計數器cx從stack棧中恢復
loop s0
mov ax, 4c00H
int 21H
code ends
end start
程序運行結果:
-d ds:0
0B65:0000 31 2E 20 44 49 53 50 6C-61 79 20 20 20 20 20 20 1. DISPlay
0B65:0010 32 2E 20 42 52 4F 57 73-20 20 20 20 20 20 20 20 2. BROWs
0B65:0020 33 2E 20 52 45 50 4C 61-63 65 20 20 20 20 20 20 3. REPLace
0B65:0030 34 2E 20 4D 4F 44 49 66-79 20 20 20 20 20 20 20 4. MODIfy
