53
單單 單單 9: 9: 單單單單單 單單單單單

單元 9: 字串與陣列

  • 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

Page 1: 單元 9: 字串與陣列

單元單元 9:9:字串與陣列字串與陣列

Page 2: 單元 9: 字串與陣列

單元概要單元概要

• 字串的基本指令• 經過篩選的字串程序 • 二維陣列 • 整數陣列的搜尋和排序

Page 3: 單元 9: 字串與陣列

字串的基本指令

• MOVSB, MOVSW, 和 MOVSD• CMPSB, CMPSW, 和 CMPSD• SCASB, SCASW, 和 SCASD• STOSB, STOSW, 和 STOSD• LODSB, LODSW, 和 LODSD

Page 4: 單元 9: 字串與陣列

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

Page 5: 單元 9: 字串與陣列

MOVSB, MOVSW, MOVSB, MOVSW, 和和 MOVSDMOVSD (2 (2 之之 2)2)

• 方向旗標將用於決定 ESI 和 EDI 會遞增或遞減• MOVSB 遞增或遞減 1

• MOVSW 遞增或遞減 2

• MOVSD 遞增或遞減 4

Page 6: 單元 9: 字串與陣列

方向旗標

• 字串基本指令會根據方向旗標的狀態,來遞增或遞減 ESI 與 EDI 。 • DF = clear (0): 遞增 ESI and EDI

• DF = set (1): 遞減 ESI and EDI

程式設計員能使用 CLD 和 STD 指令,明確地更改方向旗標:

CLD ; 使方向旗標處於清除狀態 ( 順向 )

STD ; 使方向旗標處於設定狀態 ( 逆向 )

Page 7: 單元 9: 字串與陣列

使用重覆性指令前置字使用重覆性指令前置字

• 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

Page 8: 單元 9: 字串與陣列

練習練習• 使用 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

Page 9: 單元 9: 字串與陣列

CMPSB, CMPSW, CMPSB, CMPSW, 和 和 CMPSDCMPSD

• CMPSB 、 CMPSW 和 CMPSD 指令都用於,將由 ESI 所指向的記憶體運算元,與由 EDI 所指向的記憶體運算元進行比較• CMPSB 比較位元組 • CMPSW 比較字組 • CMPSD 比較雙字組

• 重複性指令前置字 (REP) 經常的被使用

Page 10: 單元 9: 字串與陣列

比較雙字組資料

.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

Page 11: 單元 9: 字串與陣列

練習練習

• 修正程式在先前幻燈片宣佈資料和目標如 WORD 變數 , 作任何其他必要的改變 .

Page 12: 單元 9: 字串與陣列

比較數組比較數組

.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 等於零, 或發現任何一對雙字組是不相同的為止。

Page 13: 單元 9: 字串與陣列

範例範例 : : 比較二個字串比較二個字串 (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:

Page 14: 單元 9: 字串與陣列

範例範例 : : 比較二個字串比較二個字串 (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

Page 15: 單元 9: 字串與陣列

範例範例 : : 比較二個字串比較二個字串 (3(3 之之 3)3)

• ESI 和 EDI 所指向的位置,是位於發現兩個字串是不同的位置處的後面一個位置。

Page 16: 單元 9: 字串與陣列

練習練習

• 從先前二個幻燈片修正字串對照程式 , 為資料和目的字串促使使用者 .

• 樣本輸出 :

Input first string: ABCDEFGInput second string: ABCDDG

The first string is not smaller.

Page 17: 單元 9: 字串與陣列

控制螢幕(樣板)控制螢幕(樣板)

Source is smaller

Page 18: 單元 9: 字串與陣列

SCASB, SCASW, SCASB, SCASW, 和 和 SCASDSCASD

• SCASB 、 SCASW 和 SCASD 指令分別用於,將 AL/AX/EAX 中的值與由 EDI 所指向的位 元組、字組或雙字組,進行比較。

• 想在字串或陣列中尋找某單一個值的時候,這些指令會特別有用:• 在一些長字串或陣列中尋找特定的要素。• Search for the first element that does not match a given

value.

Page 19: 單元 9: 字串與陣列

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 指令的用途是什麼 ?

Page 20: 單元 9: 字串與陣列

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 的內容值

Page 21: 單元 9: 字串與陣列

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

Page 22: 單元 9: 字串與陣列

陣列乘法的範例

.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

以下的程式會將雙字組陣列的每個元素,乘以一個常數值。

Page 23: 單元 9: 字串與陣列

練習練習• 寫個程式轉換各自取出二進制編碼的十進制位元屬於

陣列進入 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(?)

Page 24: 單元 9: 字串與陣列

經過篩選的字串程序

• Str_compare 程序• Str_length 程序• Str_copy 程序• Str_trim 程序• Str_ucase 程序

我們將示範說明幾個能操作空字元終止字串 (null-terminated strings) 的程序, 其中,這些程序都是選自 Irvine32 和 Irvine16 函式庫。

Page 25: 單元 9: 字串與陣列

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

Page 26: 單元 9: 字串與陣列

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

Page 27: 單元 9: 字串與陣列

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

Page 28: 單元 9: 字串與陣列

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

Page 29: 單元 9: 字串與陣列

Str_copy Str_copy 程序程序

• Str_copy 程序用於從來源運算元所定址的位置,複製一個空字元終止字串,到目的運算元 所定址的位置。

• 示範 :

請參看 CopyStr.asm 程式來作為這個程序的示範解說。

Str_copy PROTO, source:PTR BYTE, ; pointer to string target:PTR BYTE ; pointer to string

Page 30: 單元 9: 字串與陣列

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

Page 31: 單元 9: 字串與陣列

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"

範例 :

Page 32: 單元 9: 字串與陣列

Str_trim Str_trim 程序程序

• 這個程式的邏輯很有趣,因為讀者必須檢查幾種可能的情況 ( 這裡以 # 代表被指定的尾隨 字元 ) : • 字串是空的。 • 字串含有其他字元,而且其後接著一個或多個尾隨字元,

例如 “ Hello##” 。 • 字串只含有一個字元,而這個字元即為尾隨字元,例如

"#" 。 • 字串沒有尾隨字元,例如 "Hello" 或 "H" 。 • 字串含有一個或多個尾隨字元,而且這些尾隨字元之後還

接著一個或多個非尾隨字元, 例如 "#H" 或 "###Hello" 。

Page 33: 單元 9: 字串與陣列

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

Page 34: 單元 9: 字串與陣列

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

範例 :

Page 35: 單元 9: 字串與陣列

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

Page 36: 單元 9: 字串與陣列

二維陣列

• 基底 -索引運算元 • 基底 -索引 - 移位運算元

Page 37: 單元 9: 字串與陣列

基底 -索引運算元

• 基底 - 索引運算元會將兩個暫存器的值相加,並且產生一個位移位址,其中的兩個暫存器稱 為基底 (base) 暫存器和索引 (index) 暫存器。

• 任何 32 位元通用暫存器 都可以當作基底和索引暫存器。

• 在 16 位元模式下,基底暫存器必須是 BX 或 BP 。 ( 不過,除非是要定址堆疊記憶體,否則應該避免使用 BP 或 EBP 。 )

Page 38: 單元 9: 字串與陣列

應用結構應用結構

一個基礎索引的普通申請演說必須做結構 (第 10 章 ) 的陣列發表演說 . 下列的 definds 叫做包含 X 和 Y 的 COORD 的結構屏幕坐標 :

COORD STRUCT X WORD ? ; offset 00 Y WORD ? ; offset 02COORD ENDS

.datasetOfCoordinates COORD 10 DUP(<>)

然後我們能定義 COORD 目標的陣列 :

Page 39: 單元 9: 字串與陣列

應用結構應用結構

下列的編碼使陣列成迴路而且顯示各自 Y 的座標 :

mov ebx,OFFSET setOfCoordinatesmov esi,2 ; offset of Y valuemov eax,0

L1:mov ax,[ebx+esi]call WriteDecadd ebx,SIZEOF COORDloop L1

Page 40: 單元 9: 字串與陣列

基底 -索引 - 移位運算元

• A 基底 - 索引 - 移位運算元會將移位、基底暫存器、索引暫存器和可選用的比例因子組合起 來,產生一個有效位址。

• 任何 32 位元 通用暫存器都可以當作基底和索引暫存器。 • 常見的編排 :

[ base + index + displacement ]

displacement [ base + index ]

Page 41: 單元 9: 字串與陣列

雙字組陣列的例子

想像一張表格用三列和五行資料 , 能在圖上被以任何的格式安排 :

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

選擇格式 :

Page 42: 單元 9: 字串與陣列

雙字組陣列的例子

下列的編碼填上在表格元素儲存在一列二行 :

RowNumber = 1ColumnNumber = 2

mov ebx,NumCols * RowNumbermov esi,ColumnNumbermov al,table[ebx + esi]

Page 43: 單元 9: 字串與陣列

整數陣列的搜尋和排序

• 氣泡排序• 氣泡排序法 (bubble sort) 會從位置 0 和位置 1 開

始,比較陣列中的每對陣列值。 • 二元搜尋

• 在平常的程式設計應用中,陣列搜尋是最常用的資料操作的其中一部份。 對於一個小陣列而言,執行循序搜尋是很容易的;在 循序搜尋的過程中,會從陣列的開端開始進行,然後依序檢視陣列中的每個元素,直到發現符合搜尋條件的元素為止。

Page 44: 單元 9: 字串與陣列

氣泡排序氣泡排序如果被比 較的兩個值是相反順序的,那麼它們將會交換位置,下圖進行氣泡排序法 時,一次完整通過整數陣列的處理過程

Page 45: 單元 9: 字串與陣列

氣泡排序虛擬碼氣泡排序虛擬碼我們將使用 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}

Page 46: 單元 9: 字串與陣列

完成氣泡排序完成氣泡排序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

Page 47: 單元 9: 字串與陣列

二維搜尋二維搜尋• 使用稱為 first 和 last 的下標,來標示陣列的搜尋範圍。 如果

first > last ,則跳出搜尋的 程序,並且指出,無法找到吻合的元素。

• 計算介於陣列下標 first 和 last 之間的陣列中點。

• 將 searchVal 與位於陣列中點的整數進行比較: • 如果兩個值相等,則從程序中回傳這個中點的位置到 EAX 。

這 個回傳值代表,已經從陣列中 找到符合條件的值。 • 另一方面,如果 searchVal 比位於中點的值大,那麼便將陣列

下標 first 重新設定為,比中點高一個位置的位置。 • 或者,如果 searchVal 比位於中點的值小,那麼便將陣列下標

last 重新設定為,比中點低一個 位置的位置。

• 二元搜尋都會被描述成是一種 O(log n) 演算法 :• 當陣列元素個數增加為 n 倍時,平均搜尋時間只會增加為

log n 倍。

Page 48: 單元 9: 字串與陣列

二維搜尋判斷二維搜尋判斷

Page 49: 單元 9: 字串與陣列

二維搜尋虛擬碼二維搜尋虛擬碼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

}

Page 50: 單元 9: 字串與陣列

完成二維搜尋 完成二維搜尋 (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

Page 51: 單元 9: 字串與陣列

完成二維搜尋 完成二維搜尋 (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

Page 52: 單元 9: 字串與陣列

完成二維搜尋 完成二維搜尋 (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

Page 53: 單元 9: 字串與陣列

The EndThe End