1 / 53

單元 9: 字串與陣列

單元 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

sevita
Download Presentation

單元 9: 字串與陣列

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 單元9:字串與陣列

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

  3. 字串的基本指令 • MOVSB, MOVSW, 和MOVSD • CMPSB, CMPSW, 和CMPSD • SCASB, SCASW, 和SCASD • STOSB, STOSW, 和STOSD • LODSB, LODSW, 和LODSD

  4. MOVSB, MOVSW, 和MOVSD (2之1) • MOVSB、MOVSW 和 MOVSD 指令用於從由 ESI 所指向的記憶體位置,複製資料到由 EDI 所指向的記憶體位置。 .data source DWORD 0FFFFFFFFh target DWORD ? .code mov esi,OFFSET source mov edi,OFFSET target movsd

  5. MOVSB, MOVSW, 和MOVSD (2之2) • 方向旗標將用於決定 ESI 和 EDI 會遞增或遞減 • MOVSB 遞增或遞減 1 • MOVSW 遞增或遞減 2 • MOVSD 遞增或遞減 4

  6. 方向旗標 • 字串基本指令會根據方向旗標的狀態,來遞增或遞減 ESI 與 EDI。 • DF = clear (0): 遞增 ESI and EDI • DF = set (1): 遞減 ESI and EDI 程式設計員能使用 CLD 和 STD 指令,明確地更改方向旗標: CLD ; 使方向旗標處於清除狀態 ( 順向 ) STD ; 使方向旗標處於設定狀態 ( 逆向 )

  7. 使用重覆性指令前置字 • REP 指令執行在 MOVSB, MOVSW, or MOVSD之前。 • 這個指令將會使用ECX作為計數器,來執行重複的動作。 • 範例:複製 20 個雙字組整數到 target .data source DWORD 20 DUP(?) target DWORD 20 DUP(?) .code cld ; direction = forward mov ecx,LENGTHOF source ; set REP counter mov esi,OFFSET source mov edi,OFFSET target rep movsd

  8. 練習 • 使用 MOVSD 刪除下列 doubleword 陣列的第一元素.所有的後來陣列值一定要對於開始向前移動一個位置 • 陣列: array DWORD 1,1,2,3,4,5,6,7,8,9,10 .data array DWORD 1,1,2,3,4,5,6,7,8,9,10 .code cld mov ecx,(LENGTHOF array) - 1 mov esi,OFFSET array+4 mov edi,OFFSET array rep movsd

  9. CMPSB, CMPSW, 和 CMPSD • CMPSB、CMPSW 和 CMPSD 指令都用於,將由 ESI 所指向的記憶體運算元,與由 EDI 所指向的記憶體運算元進行比較 • CMPSB 比較位元組 • CMPSW 比較字組 • CMPSD 比較雙字組 • 重複性指令前置字 (REP) 經常的被使用

  10. 比較雙字組資料 如果 source > target,則跳越到 L1;如果 source <= target,則跳越到 L2 .data source DWORD 1234h target DWORD 5678h .code mov esi,OFFSET source mov edi,OFFSET target cmpsd ;比較雙字組資料 ja L1 ;如果 source > target,則跳越到 L1 jmp L2 ;如果 source <= target,則跳越到 L2

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

  12. 比較數組 REPE 字首會重複執行比較運算,並且自動地增加 ESI 和 EDI 的值,直到 ECX 等於零, 或發現任何一對雙字組是不相同的為止。 .data source DWORD COUNT DUP(?) target DWORD COUNT DUP(?) .code mov ecx,COUNT ; 重複動作的計數器 mov esi,OFFSET source mov edi,OFFSET target cld ; 方向 = 順向 repe cmpsd ; 在相等時持續執行 cmpsd

  13. Source is smaller Screen output: 範例: 比較二個字串(3之1) 下列的程式使用 CMPSB,去比較兩個相同長度的字串。 而 REPE 指令前置字會導致 CMPSB 持續遞增 ESI 和 EDI 的值,並且一個接著一個地比較字元,直到在兩字串之間發 現不同字元為止: .data source BYTE "MARTIN " dest BYTE "MARTINEZ" str1 BYTE "Source is smaller",0dh,0ah,0 str2 BYTE "Source is not smaller",0dh,0ah,0

  14. 範例: 比較二個字串(3之2) • .code • main PROC • cld ; direction = forward • mov esi,OFFSET source • mov edi,OFFSET dest • mov cx,LENGTHOF source • repe cmpsb • jb source_smaller • mov edx,OFFSET str2 ; "source is not smaller" • jmp done • source_smaller: • mov edx,OFFSET str1 ; "source is smaller" • done: • call WriteString • exit • main ENDP • END main

  15. 範例: 比較二個字串(3之3) • ESI 和 EDI 所指向的位置,是位於發現兩個字串是不同的位置處的後面一個位置。

  16. 練習 • 從先前二個幻燈片修正字串對照程式,為資料和目的字串促使使用者. • 樣本輸出: Input first string: ABCDEFG Input second string: ABCDDG The first string is not smaller.

  17. 控制螢幕(樣板) Source is smaller

  18. SCASB, SCASW, 和 SCASD • SCASB、SCASW 和 SCASD 指令分別用於,將 AL/AX/EAX 中的值與由 EDI 所指向的位 元組、字組或雙字組,進行比較。 • 想在字串或陣列中尋找某單一個值的時候,這些指令會特別有用: • 在一些長字串或陣列中尋找特定的要素。 • Search for the first element that does not match a given value.

  19. SCASB 範例 在字串alpha中尋找字母 'F' : .data alpha BYTE "ABCDEFGH",0 .code mov edi,OFFSET alpha mov al,'F' ; search for 'F' mov ecx,LENGTHOF alpha cld repne scasb ; repeat while not equal jnz quit dec edi ; EDI points to 'F' JNZ 指令的用途是什麼?

  20. STOSB, STOSW, and STOSD • STOSB、STOSW 和 STOSD 指令分別用於,將 AL/AX/EAX 的內容存放在記憶體中,由 EDI 所指向的位移處。 • 範例:以下的程式碼會將 string1 中的每個位元組,初始化為 0FFh .data Count = 100 string1 BYTE Count DUP(?) .code mov al,0FFh ; 所要存放的值 mov edi,OFFSET string1 ; EDI 指向目標運算元 mov ecx,Count ; 字元的個數 cld ; 方向 = 順向 rep stosb ; 填入 AL 的內容值

  21. LODSB, LODSW, and LODSD • LODSB、LODSW 和 LODSD 指令會從記憶體中由 ESI 所指向的位置,載入一個位元組、 字組或雙字組,到 AL/AX/EAX 中。 .data array 1,2,3,4,5,6,7,8,9 dest 9 DUP(?) .code mov esi,OFFSET array mov edi,OFFSET dest mov ecx,LENGTHOF array cld L1: lodsb or al,30h stosb loop L1

  22. 陣列乘法的範例 以下的程式會將雙字組陣列的每個元素,乘以一個常數值。 .data array DWORD 1,2,3,4,5,6,7,8,9,10 multiplier DWORD 10 .code cld ; direction = up mov esi,OFFSET array ; source index mov edi,esi ; destination index mov ecx,LENGTHOF array ; loop counter L1: lodsd ; copy [ESI] into EAX mul multiplier ; multiply by a value stosd ; store EAX at [EDI] loop L1

  23. 練習 • 寫個程式轉換各自取出二進制編碼的十進制位元屬於陣列進入ASCII十進位位元而且複製到新的陣列. .data array BYTE 1,2,3,4,5,6,7,8,9 dest BYTE (LENGTHOF array) DUP(?) mov esi,OFFSET array mov edi,OFFSET dest mov ecx,LENGTHOF array cld L1: lodsb ; load into AL or al,30h ; convert to ASCII stosb ; store into memory loop L1

  24. 經過篩選的字串程序 • Str_compare 程序 • Str_length 程序 • Str_copy 程序 • Str_trim 程序 • Str_ucase 程序 我們將示範說明幾個能操作空字元終止字串 (null-terminated strings) 的程序, 其中,這些程序都是選自 Irvine32 和 Irvine16 函式庫。

  25. Str_compare 程序 • Str_compare 程序用於比較兩個字串 • 這個程序並沒有回傳值,不過,進位和零值旗標可以詮釋此程序處理的結果 • 示範: Str_compare PROTO, string1:PTR BYTE, ; pointer to string string2:PTR BYTE ; pointer to string 在範例, 如果 string1 > string2, CF=0, ZF=0 或著如果 string1 < string2, CF=1, ZF=0

  26. Str_compare 來源編碼 Str_compare PROC USES eax edx esi edi, string1:PTR BYTE, string2:PTR BYTE mov esi,string1 mov edi,string2 L1: 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: ret Str_compare ENDP

  27. 範例: .data myString BYTE "abcdefg",0 .code INVOKE Str_length, ADDR myString ; EAX = 7 Str_length 程序 • Str_length 程序用於回傳字串的長度到 EAX 暫存器中。 • 示範: Str_length PROTO, pString:PTR BYTE ; pointer to string

  28. 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: ret Str_length ENDP

  29. Str_copy 程序 • Str_copy 程序用於從來源運算元所定址的位置,複製一個空字元終止字串,到目的運算元 所定址的位置。 • 示範: Str_copy PROTO, source:PTR BYTE, ; pointer to string target:PTR BYTE ; pointer to string 請參看 CopyStr.asm 程式來作為這個程序的示範解說。

  30. 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

  31. 範例: .data myString BYTE "Hello###",0 .code INVOKE Str_trim, ADDR myString myString = "Hello" Str_trim 程序 • Str_trim程序用於將空字元終止字串中被指定的尾隨字元全部移除掉。 • 示範: Str_trim PROTO, pString:PTR BYTE, ; points to string char:BYTE ; char to remove

  32. Str_trim 程序 • 這個程式的邏輯很有趣,因為讀者必須檢查幾種可能的情況 ( 這裡以 # 代表被指定的尾隨 字元 ): • 字串是空的。 • 字串含有其他字元,而且其後接著一個或多個尾隨字元,例如 “Hello##”。 • 字串只含有一個字元,而這個字元即為尾隨字元,例如 "#"。 • 字串沒有尾隨字元,例如 "Hello" 或 "H"。 • 字串含有一個或多個尾隨字元,而且這些尾隨字元之後還接著一個或多個非尾隨字元, 例如 "#H" 或 "###Hello"。

  33. Str_trim 來源編碼 Str_trim PROC USES eax ecx edi, pString:PTR BYTE, ;指向字串的指標 char:BYTE ;要移除的字元 mov edi,pString INVOKE Str_length,edi ;將字串長度回傳到 EAX cmp eax,0 ;字串長度為零嗎? je L2 ;是: 則離開此程序 mov ecx,eax ;否: 計數器 = 字串長度 dec eax add 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: ret Str_trim ENDP

  34. 範例: .data myString BYTE "Hello",0 .code INVOKE Str_ucase, ADDR myString Str_ucase 程序 • Str_ucase 程序用於將一個字串,全都轉換成大寫的字元。 而且,這個程序不會回傳任何 值。 • 示範: Str_ucase PROTO, pString:PTR BYTE ; pointer to string

  35. Str_ucase 來源編碼 Str_ucase PROC USES eax esi, pString:PTR BYTE mov esi,pString L1: mov al,[esi] ;取得字元 cmp al,0 ;字串的尾端? je L3 ;是: 則離開此程序 cmp al,'a' ;字元的值小於 "a" 嗎? jb L2 cmp al,'z' ;字元的值大於 "z" 嗎? ja L2 and BYTE PTR [esi],11011111b ;轉換為大寫字元 L2: inc esi ;指向下一個字元 jmp L1 L3: ret Str_ucase ENDP

  36. 二維陣列 • 基底-索引運算元 • 基底-索引-移位運算元

  37. 基底-索引運算元 • 基底 - 索引運算元會將兩個暫存器的值相加,並且產生一個位移位址,其中的兩個暫存器稱 為基底 (base) 暫存器和索引 (index) 暫存器。 • 任何 32 位元通用暫存器 都可以當作基底和索引暫存器。 • 在 16 位元模式下,基底暫存器必須是 BX 或 BP。 ( 不過,除非是要定址堆疊記憶體,否則應該避免使用 BP 或 EBP。)

  38. 應用結構 一個基礎索引的普通申請演說必須做結構 (第 10 章) 的陣列發表演說.下列的 definds 叫做包含 X 和 Y 的 COORD 的結構屏幕坐標 : COORD STRUCT X WORD ? ; offset 00 Y WORD ? ; offset 02 COORD ENDS 然後我們能定義 COORD 目標的陣列: .data setOfCoordinates COORD 10 DUP(<>)

  39. 應用結構 下列的編碼使陣列成迴路而且顯示各自Y的座標: mov ebx,OFFSET setOfCoordinates mov esi,2 ; offset of Y value mov eax,0 L1: mov ax,[ebx+esi] call WriteDec add ebx,SIZEOF COORD loop L1

  40. 基底-索引-移位運算元 • A基底 - 索引 - 移位運算元會將移位、基底暫存器、索引暫存器和可選用的比例因子組合起 來,產生一個有效位址。 • 任何 32 位元 通用暫存器都可以當作基底和索引暫存器。 • 常見的編排: [ base + index + displacement ] displacement [ base + index ]

  41. 選擇格式: table BYTE 10h,20h,30h,40h,50h,60h,70h, 80h,90h,0A0h, 0B0h,0C0h,0D0h, 0E0h,0F0h NumCols = 5 雙字組陣列的例子 想像一張表格用三列和五行資料,能在圖上被以任何的格式安排: table BYTE 10h, 20h, 30h, 40h, 50h BYTE 60h, 70h, 80h, 90h, 0A0h BYTE 0B0h, 0C0h, 0D0h, 0E0h, 0F0h NumCols = 5

  42. 雙字組陣列的例子 下列的編碼填上在表格元素儲存在一列二行: RowNumber = 1 ColumnNumber = 2 mov ebx,NumCols * RowNumber mov esi,ColumnNumber mov al,table[ebx + esi]

  43. 整數陣列的搜尋和排序 • 氣泡排序 • 氣泡排序法 (bubble sort) 會從位置 0 和位置 1 開始,比較陣列中的每對陣列值。 • 二元搜尋 • 在平常的程式設計應用中,陣列搜尋是最常用的資料操作的其中一部份。 對於一個小陣列而言,執行循序搜尋是很容易的;在 循序搜尋的過程中,會從陣列的開端開始進行,然後依序檢視陣列中的每個元素,直到發現符合搜尋條件的元素為止。

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

  45. 氣泡排序虛擬碼 我們將使用 N 來代表陣列的大小,使用 cx1 來代表外層迴圈計數器,而 cx2 則表 示內層迴圈計數器: cx1 = N - 1 while( 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 }

  46. 完成氣泡排序 BubbleSort PROC USES eax ecx esi, pArray:PTR DWORD,Count:DWORD mov ecx,Count dec ecx ; decrement count by 1 L1: push ecx ; save outer loop count mov esi,pArray ; point to first value L2: mov eax,[esi] ; get array value cmp [esi+4],eax ; compare a pair of values jge L3 ; if [esi] <= [edi], skip xchg eax,[esi+4] ; else exchange the pair mov [esi],eax L3: add esi,4 ; move both pointers forward loop L2 ; inner loop pop ecx ; retrieve outer loop count loop L1 ; else repeat outer loop L4: ret BubbleSort ENDP

  47. 二維搜尋 • 使用稱為 first 和 last 的下標,來標示陣列的搜尋範圍。 如果 first > last,則跳出搜尋的 程序,並且指出,無法找到吻合的元素。 • 計算介於陣列下標 first 和 last 之間的陣列中點。 • 將 searchVal 與位於陣列中點的整數進行比較: • 如果兩個值相等,則從程序中回傳這個中點的位置到 EAX。 這 個回傳值代表,已經從陣列中 找到符合條件的值。 • 另一方面,如果 searchVal 比位於中點的值大,那麼便將陣列下標 first 重新設定為,比中點高一個位置的位置。 • 或者,如果 searchVal 比位於中點的值小,那麼便將陣列下標last 重新設定為,比中點低一個 位置的位置。 • 二元搜尋都會被描述成是一種 O(log n) 演算法: • 當陣列元素個數增加為 n 倍時,平均搜尋時間只會增加為 log n 倍。

  48. 二維搜尋判斷

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

  50. 完成二維搜尋 (1 之 3) BinarySearch PROC uses ebx edx esi edi, pArray:PTR DWORD, ; pointer to array Count:DWORD, ; array size searchVal:DWORD ; search value LOCAL first:DWORD, ; first position last:DWORD, ; last position mid:DWORD ; midpoint mov first,0 ; first = 0 mov eax,Count ; last = (count - 1) dec eax mov last,eax mov edi,searchVal ; EDI = searchVal mov ebx,pArray ; EBX points to the array L1: ; while first <= last mov eax,first cmp eax,last jg L5 ; exit search

More Related