Upload
sevita
View
91
Download
0
Embed Size (px)
DESCRIPTION
單元 9: 字串與陣列. 單元概要. 字串的基本指令 經過篩選的字串程序 二維陣列 整數陣列的搜尋和排序. 字串的基本指令. MOVSB, MOVSW, 和 MOVSD CMPSB, CMPSW, 和 CMPSD SCASB, SCASW, 和 SCASD STOSB, STOSW, 和 STOSD LODSB, LODSW, 和 LODSD. MOVSB, MOVSW, 和 MOVSD (2 之 1). MOVSB、MOVSW 和 MOVSD 指令用於從由 ESI 所指向的記憶體位置,複製資料到由 EDI 所指向的記憶體位置。. .data - PowerPoint PPT Presentation
Citation preview
單元單元 9:9:字串與陣列字串與陣列
單元概要單元概要
• 字串的基本指令• 經過篩選的字串程序 • 二維陣列 • 整數陣列的搜尋和排序
字串的基本指令
• MOVSB, MOVSW, 和 MOVSD• CMPSB, CMPSW, 和 CMPSD• SCASB, SCASW, 和 SCASD• STOSB, STOSW, 和 STOSD• LODSB, LODSW, 和 LODSD
MOVSB, MOVSW, MOVSB, MOVSW, 和和 MOVSDMOVSD (2 (2 之之 1)1)
• MOVSB 、 MOVSW 和 MOVSD 指令用於從由 ESI 所指向的記憶體位置,複製資料到由 EDI 所指向的記憶體位置。
.datasource DWORD 0FFFFFFFFhtarget DWORD ?.codemov esi,OFFSET sourcemov edi,OFFSET targetmovsd
MOVSB, MOVSW, MOVSB, MOVSW, 和和 MOVSDMOVSD (2 (2 之之 2)2)
• 方向旗標將用於決定 ESI 和 EDI 會遞增或遞減• MOVSB 遞增或遞減 1
• MOVSW 遞增或遞減 2
• MOVSD 遞增或遞減 4
方向旗標
• 字串基本指令會根據方向旗標的狀態,來遞增或遞減 ESI 與 EDI 。 • DF = clear (0): 遞增 ESI and EDI
• DF = set (1): 遞減 ESI and EDI
程式設計員能使用 CLD 和 STD 指令,明確地更改方向旗標:
CLD ; 使方向旗標處於清除狀態 ( 順向 )
STD ; 使方向旗標處於設定狀態 ( 逆向 )
使用重覆性指令前置字使用重覆性指令前置字
• REP 指令執行在 MOVSB, MOVSW, or MOVSD 之前。 • 這個指令將會使用 ECX 作為計數器,來執行重複的動作。• 範例 : 複製 20 個雙字組整數到 target
.datasource DWORD 20 DUP(?)target DWORD 20 DUP(?).codecld ; direction = forwardmov ecx,LENGTHOF source ; set REP countermov esi,OFFSET sourcemov edi,OFFSET targetrep movsd
練習練習• 使用 MOVSD 刪除下列 doubleword 陣列的第一元素.
所有的後來陣列值一定要對於開始向前移動一個位置• 陣列 :
array DWORD 1,1,2,3,4,5,6,7,8,9,10
.dataarray DWORD 1,1,2,3,4,5,6,7,8,9,10.codecldmov ecx,(LENGTHOF array) - 1mov esi,OFFSET array+4mov edi,OFFSET arrayrep movsd
CMPSB, CMPSW, CMPSB, CMPSW, 和 和 CMPSDCMPSD
• CMPSB 、 CMPSW 和 CMPSD 指令都用於,將由 ESI 所指向的記憶體運算元,與由 EDI 所指向的記憶體運算元進行比較• CMPSB 比較位元組 • CMPSW 比較字組 • CMPSD 比較雙字組
• 重複性指令前置字 (REP) 經常的被使用
比較雙字組資料
.datasource DWORD 1234htarget DWORD 5678h
.codemov esi,OFFSET sourcemov edi,OFFSET targetcmpsd ; 比較雙字組資料 ja L1 ; 如果 source > target ,則跳越到 L1 jmp L2 ; 如果 source <= target ,則跳越到 L2
如果 source > target ,則跳越到 L1; 如果 source <= target ,則跳越到 L2
練習練習
• 修正程式在先前幻燈片宣佈資料和目標如 WORD 變數 , 作任何其他必要的改變 .
比較數組比較數組
.datasource DWORD COUNT DUP(?)target DWORD COUNT DUP(?).codemov ecx,COUNT ; 重複動作的計數器 mov esi,OFFSET sourcemov edi,OFFSET targetcld ; 方向 = 順向 repe cmpsd ; 在相等時持續執行 cmpsd
REPE 字首會重複執行比較運算,並且自動地增加 ESI 和 EDI 的值,直到 ECX 等於零, 或發現任何一對雙字組是不相同的為止。
範例範例 : : 比較二個字串比較二個字串 (3(3 之之 1)1)
.datasource BYTE "MARTIN "dest BYTE "MARTINEZ"str1 BYTE "Source is smaller",0dh,0ah,0str2 BYTE "Source is not smaller",0dh,0ah,0
下列的程式使用 CMPSB ,去比較兩個相同長度的字串。 而 REPE 指令前置字會導致 CMPSB 持續遞增 ESI 和 EDI 的值,並且一個接著一個地比較字元,直到在兩字串之間發 現不同字元為止:
Source is smallerScreen output:
範例範例 : : 比較二個字串比較二個字串 (3(3 之之 2)2)
.codemain PROC
cld ; direction = forwardmov esi,OFFSET sourcemov edi,OFFSET destmov cx,LENGTHOF sourcerepe cmpsbjb source_smallermov edx,OFFSET str2 ; "source is not smaller"jmp done
source_smaller:mov edx,OFFSET str1 ; "source is smaller"
done:call WriteStringexit
main ENDPEND main
範例範例 : : 比較二個字串比較二個字串 (3(3 之之 3)3)
• ESI 和 EDI 所指向的位置,是位於發現兩個字串是不同的位置處的後面一個位置。
練習練習
• 從先前二個幻燈片修正字串對照程式 , 為資料和目的字串促使使用者 .
• 樣本輸出 :
Input first string: ABCDEFGInput second string: ABCDDG
The first string is not smaller.
控制螢幕(樣板)控制螢幕(樣板)
Source is smaller
SCASB, SCASW, SCASB, SCASW, 和 和 SCASDSCASD
• SCASB 、 SCASW 和 SCASD 指令分別用於,將 AL/AX/EAX 中的值與由 EDI 所指向的位 元組、字組或雙字組,進行比較。
• 想在字串或陣列中尋找某單一個值的時候,這些指令會特別有用:• 在一些長字串或陣列中尋找特定的要素。• Search for the first element that does not match a given
value.
SCASB SCASB 範例範例
.dataalpha BYTE "ABCDEFGH",0.codemov edi,OFFSET alphamov al,'F' ; search for 'F'mov ecx,LENGTHOF alphacldrepne scasb ; repeat while not equaljnz quitdec edi ; EDI points to 'F'
在字串 alpha 中尋找字母 'F' :
JNZ 指令的用途是什麼 ?
STOSB, STOSW, and STOSDSTOSB, STOSW, and STOSD
• STOSB 、 STOSW 和 STOSD 指令分別用於,將 AL/AX/EAX 的內容存放在記憶體中,由 EDI 所指向的位移處。
• 範例 : 以下的程式碼會將 string1 中的每個位元組,初始化為 0FFh
.dataCount = 100string1 BYTE Count DUP(?).codemov al,0FFh ; 所要存放的值 mov edi,OFFSET string1 ; EDI 指向目標運算元 mov ecx,Count ; 字元的個數 cld ; 方向 = 順向 rep stosb ; 填入 AL 的內容值
LODSB, LODSW, and LODSDLODSB, LODSW, and LODSD
• LODSB 、 LODSW 和 LODSD 指令會從記憶體中由 ESI 所指向的位置,載入一個位元組、 字組或雙字組,到 AL/AX/EAX 中。
.dataarray 1,2,3,4,5,6,7,8,9dest 9 DUP(?).code
mov esi,OFFSET arraymov edi,OFFSET destmov ecx,LENGTHOF arraycld
L1: lodsbor al,30hstosbloop L1
陣列乘法的範例
.dataarray DWORD 1,2,3,4,5,6,7,8,9,10multiplier DWORD 10.code
cld ; direction = upmov esi,OFFSET array ; source indexmov edi,esi ; destination indexmov ecx,LENGTHOF array ; loop counter
L1: lodsd ; copy [ESI] into EAXmul multiplier ; multiply by a valuestosd ; store EAX at [EDI]loop L1
以下的程式會將雙字組陣列的每個元素,乘以一個常數值。
練習練習• 寫個程式轉換各自取出二進制編碼的十進制位元屬於
陣列進入 ASCII 十進位位元而且複製到新的陣列 .
mov esi,OFFSET arraymov edi,OFFSET destmov ecx,LENGTHOF arraycld
L1: lodsb ; load into ALor al,30h ; convert to ASCIIstosb ; store into memoryloop L1
.dataarray BYTE 1,2,3,4,5,6,7,8,9dest BYTE (LENGTHOF array) DUP(?)
經過篩選的字串程序
• Str_compare 程序• Str_length 程序• Str_copy 程序• Str_trim 程序• Str_ucase 程序
我們將示範說明幾個能操作空字元終止字串 (null-terminated strings) 的程序, 其中,這些程序都是選自 Irvine32 和 Irvine16 函式庫。
Str_compare Str_compare 程序程序
• Str_compare 程序用於比較兩個字串• 這個程序並沒有回傳值,不過,進位和零值旗標
可以詮釋此程序處理的結果• 示範 :
在範例 ,
如果 string1 > string2, CF=0, ZF=0
或著如果 string1 < string2, CF=1, ZF=0
Str_compare PROTO, string1:PTR BYTE, ; pointer to string string2:PTR BYTE ; pointer to string
Str_compare Str_compare 來源編碼來源編碼Str_compare PROC USES eax edx esi edi,
string1:PTR BYTE, string2:PTR BYTE mov esi,string1 mov edi,string2L1: mov al,[esi] mov dl,[edi]
cmp al,0 ; string1 是否結束? jne L2 ; 否 cmp dl,0 ; 是: string2 是否結束? jne L2 ; 否 jmp L3 ; 是: ZF = 1 ,離開此程序 L2: inc esi ; 指向下一個位置 inc edi
cmp al,dl ; 兩個字元相同嗎? je L1 ; 是:繼續執行迴圈 L3: retStr_compare ENDP
Str_length Str_length 程序程序
• Str_length 程序用於回傳字串的長度到 EAX 暫存器中。
• 示範 :
.datamyString BYTE "abcdefg",0.code
INVOKE Str_length, ADDR myString
; EAX = 7
範例:
Str_length PROTO, pString:PTR BYTE ; pointer to string
Str_length Str_length 來源編碼來源編碼
Str_length PROC USES edi,
pString:PTR BYTE ; 指向字串的指標
mov edi,pString
mov eax,0 ; 字元計數器 L1:
cmp byte ptr [edi],0 ; 字串尾端了嗎 ? je L2 ; 是:則離開 inc edi ; 否:指向下一個字元 inc eax ; 將計數器加 1 jmp L1
L2: retStr_length ENDP
Str_copy Str_copy 程序程序
• Str_copy 程序用於從來源運算元所定址的位置,複製一個空字元終止字串,到目的運算元 所定址的位置。
• 示範 :
請參看 CopyStr.asm 程式來作為這個程序的示範解說。
Str_copy PROTO, source:PTR BYTE, ; pointer to string target:PTR BYTE ; pointer to string
Str_copy Str_copy 來源編碼來源編碼
Str_copy PROC USES eax ecx esi edi,
source:PTR BYTE, ; 來源字串 target:PTR BYTE ; 目的字串
INVOKE Str_length,source ;EAX = 來源字串的長度 mov ecx,eax ;REP 的執行次數inc ecx ;針對空位元組而加 1 mov esi,source
mov edi,target
cld ; 方向 = 順向 rep movsb ; 複製字串 ret
Str_copy ENDP
Str_trim Str_trim 程序程序
• Str_trim 程序用於將空字元終止字串中被指定的尾隨字元全部移除掉。
• 示範 :
Str_trim PROTO, pString:PTR BYTE, ; points to string char:BYTE ; char to remove
.datamyString BYTE "Hello###",0.code
INVOKE Str_trim, ADDR myString
myString = "Hello"
範例 :
Str_trim Str_trim 程序程序
• 這個程式的邏輯很有趣,因為讀者必須檢查幾種可能的情況 ( 這裡以 # 代表被指定的尾隨 字元 ) : • 字串是空的。 • 字串含有其他字元,而且其後接著一個或多個尾隨字元,
例如 “ Hello##” 。 • 字串只含有一個字元,而這個字元即為尾隨字元,例如
"#" 。 • 字串沒有尾隨字元,例如 "Hello" 或 "H" 。 • 字串含有一個或多個尾隨字元,而且這些尾隨字元之後還
接著一個或多個非尾隨字元, 例如 "#H" 或 "###Hello" 。
Str_trim Str_trim 來源編碼來源編碼Str_trim PROC USES eax ecx edi,
pString:PTR BYTE, ; 指向字串的指標 char:BYTE ; 要移除的字元 mov edi,pStringINVOKE Str_length,edi ; 將字串長度回傳到 EAX cmp eax,0 ; 字串長度為零嗎? je L2 ; 是: 則離開此程序 mov ecx,eax ; 否: 計數器 = 字串長度 dec eaxadd edi,eax ; 使 EDI 指向最後一個字元mov al,char ; 想要移除的字元 std ; 方向 = 逆向 repe scasb ; 跳過想要移除的字元 jne L1 ; 要移除第一個字元嗎? dec edi ;調整 EDI: ZF=1 && ECX=0
L1: mov BYTE PTR [edi+2],0 ;插入空位元組 L2: retStr_trim ENDP
Str_ucase Str_ucase 程序程序
• Str_ucase 程序用於將一個字串,全都轉換成大寫的字元。 而且,這個程序不會回傳任何 值。
• 示範 :
Str_ucase PROTO, pString:PTR BYTE ; pointer to string
.datamyString BYTE "Hello",0.code
INVOKE Str_ucase, ADDR myString
範例 :
Str_ucase Str_ucase 來源編碼來源編碼Str_ucase PROC USES eax esi,
pString:PTR BYTEmov esi,pString
L1: mov al,[esi] ; 取得字元 cmp al,0 ; 字串的尾端? je L3 ; 是: 則離開此程序 cmp al,'a' ; 字元的值小於 "a" 嗎? jb L2cmp al,'z' ; 字元的值大於 "z" 嗎? ja L2and BYTE PTR [esi],11011111b ; 轉換為大寫字元
L2: inc esi ; 指向下一個字元 jmp L1
L3: retStr_ucase ENDP
二維陣列
• 基底 -索引運算元 • 基底 -索引 - 移位運算元
基底 -索引運算元
• 基底 - 索引運算元會將兩個暫存器的值相加,並且產生一個位移位址,其中的兩個暫存器稱 為基底 (base) 暫存器和索引 (index) 暫存器。
• 任何 32 位元通用暫存器 都可以當作基底和索引暫存器。
• 在 16 位元模式下,基底暫存器必須是 BX 或 BP 。 ( 不過,除非是要定址堆疊記憶體,否則應該避免使用 BP 或 EBP 。 )
應用結構應用結構
一個基礎索引的普通申請演說必須做結構 (第 10 章 ) 的陣列發表演說 . 下列的 definds 叫做包含 X 和 Y 的 COORD 的結構屏幕坐標 :
COORD STRUCT X WORD ? ; offset 00 Y WORD ? ; offset 02COORD ENDS
.datasetOfCoordinates COORD 10 DUP(<>)
然後我們能定義 COORD 目標的陣列 :
應用結構應用結構
下列的編碼使陣列成迴路而且顯示各自 Y 的座標 :
mov ebx,OFFSET setOfCoordinatesmov esi,2 ; offset of Y valuemov eax,0
L1:mov ax,[ebx+esi]call WriteDecadd ebx,SIZEOF COORDloop L1
基底 -索引 - 移位運算元
• A 基底 - 索引 - 移位運算元會將移位、基底暫存器、索引暫存器和可選用的比例因子組合起 來,產生一個有效位址。
• 任何 32 位元 通用暫存器都可以當作基底和索引暫存器。 • 常見的編排 :
[ base + index + displacement ]
displacement [ base + index ]
雙字組陣列的例子
想像一張表格用三列和五行資料 , 能在圖上被以任何的格式安排 :
table BYTE 10h, 20h, 30h, 40h, 50h BYTE 60h, 70h, 80h, 90h, 0A0h BYTE 0B0h, 0C0h, 0D0h, 0E0h, 0F0hNumCols = 5
table BYTE 10h,20h,30h,40h,50h,60h,70h, 80h,90h,0A0h, 0B0h,0C0h,0D0h, 0E0h,0F0hNumCols = 5
選擇格式 :
雙字組陣列的例子
下列的編碼填上在表格元素儲存在一列二行 :
RowNumber = 1ColumnNumber = 2
mov ebx,NumCols * RowNumbermov esi,ColumnNumbermov al,table[ebx + esi]
整數陣列的搜尋和排序
• 氣泡排序• 氣泡排序法 (bubble sort) 會從位置 0 和位置 1 開
始,比較陣列中的每對陣列值。 • 二元搜尋
• 在平常的程式設計應用中,陣列搜尋是最常用的資料操作的其中一部份。 對於一個小陣列而言,執行循序搜尋是很容易的;在 循序搜尋的過程中,會從陣列的開端開始進行,然後依序檢視陣列中的每個元素,直到發現符合搜尋條件的元素為止。
氣泡排序氣泡排序如果被比 較的兩個值是相反順序的,那麼它們將會交換位置,下圖進行氣泡排序法 時,一次完整通過整數陣列的處理過程
氣泡排序虛擬碼氣泡排序虛擬碼我們將使用 N 來代表陣列的大小,使用 cx1 來代表外層迴圈計數器,而 cx2 則表 示內層迴圈計數器:
cx1 = N - 1while( cx1 > 0 ){ esi = addr(array) cx2 = cx1 while( cx2 > 0 ) { if( array[esi] < array[esi+4] ) exchange( array[esi], array[esi+4] ) add esi,4 dec cx2 } dec cx1}
完成氣泡排序完成氣泡排序BubbleSort PROC USES eax ecx esi,
pArray:PTR DWORD,Count:DWORDmov ecx,Countdec ecx ; decrement count by 1
L1: push ecx ; save outer loop countmov esi,pArray ; point to first value
L2: mov eax,[esi] ; get array valuecmp [esi+4],eax ; compare a pair of valuesjge L3 ; if [esi] <= [edi], skipxchg eax,[esi+4] ; else exchange the pairmov [esi],eax
L3: add esi,4 ; move both pointers forwardloop L2 ; inner looppop ecx ; retrieve outer loop countloop L1 ; else repeat outer loop
L4: retBubbleSort ENDP
二維搜尋二維搜尋• 使用稱為 first 和 last 的下標,來標示陣列的搜尋範圍。 如果
first > last ,則跳出搜尋的 程序,並且指出,無法找到吻合的元素。
• 計算介於陣列下標 first 和 last 之間的陣列中點。
• 將 searchVal 與位於陣列中點的整數進行比較: • 如果兩個值相等,則從程序中回傳這個中點的位置到 EAX 。
這 個回傳值代表,已經從陣列中 找到符合條件的值。 • 另一方面,如果 searchVal 比位於中點的值大,那麼便將陣列
下標 first 重新設定為,比中點高一個位置的位置。 • 或者,如果 searchVal 比位於中點的值小,那麼便將陣列下標
last 重新設定為,比中點低一個 位置的位置。
• 二元搜尋都會被描述成是一種 O(log n) 演算法 :• 當陣列元素個數增加為 n 倍時,平均搜尋時間只會增加為
log n 倍。
二維搜尋判斷二維搜尋判斷
二維搜尋虛擬碼二維搜尋虛擬碼int BinSearch( int values[],
const int searchVal, int count ){
int first = 0;int last = count - 1;while( first <= last ){
int mid = (last + first) / 2;if( values[mid] < searchVal )
first = mid + 1;else if( values[mid] > searchVal )
last = mid - 1;else
return mid; // success}return -1; // not found
}
完成二維搜尋 完成二維搜尋 (1 (1 之 之 3)3)
BinarySearch PROC uses ebx edx esi edi,pArray:PTR DWORD, ; pointer to arrayCount:DWORD, ; array sizesearchVal:DWORD ; search value
LOCAL first:DWORD, ; first positionlast:DWORD, ; last positionmid:DWORD ; midpointmov first,0 ; first = 0mov eax,Count ; last = (count - 1)dec eaxmov last,eaxmov edi,searchVal ; EDI = searchValmov ebx,pArray ; EBX points to the array
L1: ; while first <= lastmov eax,firstcmp eax,lastjg L5 ; exit search
完成二維搜尋 完成二維搜尋 (2 (2 之之 3)3)
; mid = (last + first) / 2mov eax,lastadd eax,firstshr eax,1mov mid,eax
; EDX = values[mid]mov esi,midshl esi,2 ; scale mid value by 4mov edx,[ebx+esi] ; EDX = values[mid]
; if ( EDX < searchval(EDI) ); first = mid + 1;
cmp edx,edijge L2mov eax,mid ; first = mid + 1inc eaxmov first,eaxjmp L4 ; continue the loop
base-index addressing
完成二維搜尋 完成二維搜尋 (3 (3 之 之 3)3)
; else if( EDX > searchVal(EDI) ); last = mid - 1;L2: cmp edx,edi ; (could be removed)
jle L3mov eax,mid ; last = mid - 1dec eaxmov last,eaxjmp L4 ; continue the loop
; else return midL3: mov eax,mid ; value found
jmp L9 ; return (mid)
L4: jmp L1 ; continue the loopL5: mov eax,-1 ; search failedL9: retBinarySearch ENDP
The EndThe End