汇编开发(七):字符串与数组

1. 字符串原语指令
3110861-19e65d542c3f1f78.png
String Primitive Instructions.png
  • 使用重复前缀
    如果添加重复前缀,则使用ECX作为计数器重复该指令。
3110861-5dedf65b9e173a23.png
Repeat Prefix.png
  • 复制字符串
cld                     ; clear direction flag
mov esi,OFFSET string1  ; ESI points to source
mov edi,OFFSET string2  ; EDI points to target
mov ecx,10              ; set counter to 10
rep movsb               ; move 10 bytes
  • 方向标志
    字符串基元指令根据Direction标志的状态递增或递减ESI和EDI。
CLD ; clear Direction flag (forward direction)
STD ; set Direction flag (reverse direction)
3110861-0c4a3cd61022b68e.png
Direction Flag Usage in String Primitive Instructions.png
1). MOVSB, MOVSW 和 MOVSD

MOVSB,MOVSW和MOVSD将数据从ESI指向的内存位置复制到EDI指向的内存位置。

3110861-29136c51e980ca5f.png
MOVSB, MOVSW, and MOVSD.png
  • 示例:复制DWORD数组
.data
    source DWORD 20 DUP(0FFFFFFFFh)
    target DWORD 20 DUP(?)
.code
    cld                         ; direction = forward
    mov ecx,LENGTHOF source     ; set REP counter
    mov esi,OFFSET source       ; ESI points to source
    mov edi,OFFSET target       ; EDI points to target
    rep movsd                   ; copy doublewords
2). CMPSB, CMPSW 和 CMPSD

CMPSB, CMPSW 和 CMPSD指将ESI指向的每个内存操作数都与EDI指向的内存操作数进行比较。

3110861-ab4ccb103324f733.png
CMPSB, CMPSW, and CMPSD.png
  • 示例:比较DWORD
.data
    source DWORD 1234h
    target DWORD 5678h
.code
    mov esi,OFFSET source
    mov edi,OFFSET target
    cld                         ; direction = forward
    mov ecx,LENGTHOF source     ; repetition counter
    repe cmpsd                  ; repeat while equal
3). SCASB, SCASW 和 SCASD

SCASB, SCASW和SCASD将AL / AX / EAX中的值分别与EDI寻址的字节,字或双字进行比较。这个指令可用在在字符串或者数组中查找单个字符。

  • 示例:扫描匹配字符
.data
    alpha BYTE "ABCDEFGH",0
.code
    mov edi,OFFSET alpha    ; EDI points to the string
    mov al,'F'              ; search for the letter F
    mov ecx,LENGTHOF alpha  ; set the search count
    cld                     ; direction = forward
    repne scasb             ; repeat while not equal
    jnz quit                ; quit if letter not found
    dec edi                 ; found: back up EDI
4). STOSB, STOSW 和 STOSD

STOSB, STOSW 和 STOSD指令将AL / AX / EAX的内容分别存储在EDI指向的偏移量的内存中。

.data
    Count = 100
    string1 BYTE Count DUP(?)
.code
    mov al,0FFh             ; value to be stored
    mov edi,OFFSET string1  ; EDI points to target
    mov ecx,Count           ; character count
    cld                     ; direction = forward
    rep stosb               ; fill with contents of AL
5). LODSB, LODSW 和 LODSD

LODSB, LODSW 和 LODSD指令将ESI中的内存中的字节或字分别加载到AL / AX / EAX中。

  • 示例:数组元素乘法
; 数组元素乘法

INCLUDE Irvine32.inc

.data
    array DWORD 1, 2, 3, 4, 5, 6, 7, 8, 9, 10   ; 测试数据
    multiplier DWORD 10

.code
main PROC
    cld                     ; direction = forward
    mov esi, OFFSET array   ; 数组首地址
    mov edi, esi            ; 目标序号
    mov ecx, LENGTHOF array ; 循环计数

L1:
    lodsd                   ; 加载[ESI]到EAX
    mul multiplier          ; 乘每个数据
    stosd                   ; 保存EAX到[EDI]
    LOOP L1             
    
    call Crlf
    call WaitMsg
    exit
main ENDP
END
2. 字符串处理程序
; Copy a source string to a target string.
Str_copy PROTO,
    source:PTR BYTE,
    target:PTR BYTE

; Return the length of a string (excluding the null byte) in EAX.
Str_length PROTO,
    pString:PTR BYTE

; Compare string1 to string2. Set the Zero and
; Carry flags in the same way as the CMP instruction.
Str_compare PROTO,
    string1:PTR BYTE,
    string2:PTR BYTE

; Trim a given trailing character from a string.
; The second argument is the character to trim.
Str_trim PROTO,
    pString:PTR BYTE,
    char:BYTE

; Convert a string to upper case.
Str_ucase PROTO,
    pString:PTR BYTE
1). Str_compare 程序
  • 格式
INVOKE Str_compare, ADDR string1, ADDR string2

这个函数从第一个字节开始有序的比较,它比较是敏感的,因为大小写ASCII码是不同的。函数不返回值,但是他的CF和ZF会被更改。

3110861-1a44719a1cb33b64.png
Flags Affected by the Str_compare Procedure.png
  • 程序源码
;-----------------------------------------------------------
Str_compare PROC USES eax edx esi edi,
    string1:PTR BYTE,
    string2:PTR BYTE
;
; Compares two strings.
; Returns nothing, but the Zero and Carry flags are affected
; exactly as they would be by the CMP instruction.
;-----------------------------------------------------------
    mov esi,string1
    mov edi,string2
    L1: mov al,[esi]
    mov dl,[edi]
    cmp al,0        ; end of string1?
    jne L2          ; no
    cmp dl,0        ; yes: end of string2?
    jne L2          ; no
    jmp L3          ; yes, exit with ZF = 1
    L2: inc esi     ; point to next
    inc edi
    cmp al,dl       ; characters equal?
    je L1           ; yes: continue loop
                    ; no: exit with flags set
    L3: ret
Str_compare ENDP
2). Str_length 程序

Str_length 程序将字符串的长度存储在EAX寄存器中返回,调用它的时候通过字符串的首地址。

INVOKE Str_length, ADDR myString
  • 程序源码
Str_length PROC USES edi,
    pString:PTR BYTE        ; pointer to string
    mov edi,pString
    mov eax,0 ; character count
L1: 
    cmp BYTE PTR[edi],0     ; end of string?
    je L2                   ; yes: quit
    inc edi                 ; no: point to next
    inc eax                 ; add 1 to count
    jmp L1
L2: 
    ret
Str_length ENDP
3). Str_copy 程序

Str_copy 程序从源字符串复制一个以空值终止的字符串到目标字符串。当调用这个程序的时候,你必须给目标字符串一个足够大的字节数为复制字符串。

INVOKE Str_copy, ADDR source, ADDR target
  • 程序源码
;--------------------------------------------------------
Str_copy PROC USES eax ecx esi edi,
    source:PTR BYTE, ; source string
    target:PTR BYTE ; target string
;
; Copies a string from source to target.
; Requires: the target string must contain enough
; space to hold a copy of the source string.
;--------------------------------------------------------
    INVOKE Str_length,source    ; EAX = length source
    mov ecx,eax                 ; REP count
    inc ecx                     ; add 1 for null byte
    mov esi,source
    mov edi,target
    cld                         ; direction = forward
    rep movsb                   ; copy the string
    ret
Str_copy ENDP
4). Str_trim程序

Str_trim 程序从一个以空值终止的字符串中移除所有出现的选定尾随字符。

INVOKE Str_trim, ADDR string, char_to_trim
  • 源代码
;------------------------------------------------------------
; Str_trim
; Remove all occurrences of a given delimiter
; character from the end of a string.
; Returns: nothing
;------------------------------------------------------------
Str_trim PROC USES eax ecx edi,
    pString:PTR BYTE,       ; points to string
    char: BYTE              ; character to remove

    mov edi,pString         ; prepare to call Str_length
    INVOKE Str_length,edi   ; returns the length in EAX
    cmp eax,0               ; is the length equal to zero?
    je L3                   ; yes: exit now
    mov ecx,eax             ; no: ECX = string length
    dec eax
    add edi,eax             ; point to last character
L1: 
    mov al,[edi]            ; get a character
    cmp al,char             ; is it the delimiter?
    jne L2                  ; no: insert null byte
    dec edi                 ; yes: keep backing up
    loop L1                 ; until beginning reached
L2: 
    mov BYTE PTR [edi+1],0  ; insert a null byte
L3: 
    ret
Stmr_trim ENDP
3110861-83cac489b093a8b5.png
Testing the Str_trim Procedure with a # Delimiter Character.png
5). Str_ucase 程序

Str_ucase 程序转换字符串全部为大写字符。无返回值。当调用它的时候,需要传递字符串的地址。

INVOKE Str_ucase, ADDR myString
  • 源码
;-------------------------------------------------------
; Str_ucase
; Converts a null-terminated string to uppercase.
; Returns: nothing
;-------------------------------------------------------
Str_ucase PROC USES eax esi,
    pString:PTR BYTE
    mov esi,pString
L1:
    mov al,[esi]    ; get char
    cmp al,0        ; end of string?
    je L3           ; yes: quit
    cmp al,'a'      ; below "a"?
    jb L2
    cmp al,'z'      ; above "z"?
    ja L2
    and BYTE PTR [esi],11011111b ; convert the char
L2: 
    inc esi         ; next char
    jmp L1
L3: 
    ret
Str_ucase ENDP
6). 字符串示例
; 字符串示例

INCLUDE Irvine32.inc

.data
    string_1 BYTE "abcde",0
    string_2 BYTE "ABCDE",0
    msg0 BYTE "string_1 in upper case: ",0
    msg1 BYTE "string_1 and string_2 are equal",0
    msg2 BYTE "string_1 is less than string_2",0
    msg3 BYTE "string_2 is less than string_1",0
    msg4 BYTE "Length of string_2 is ",0
    msg5 BYTE "string_1 after trimming: ",0

.code
main PROC
    
    call trim_string
    call upper_case
    call compare_strings
    call print_length
    
    call Crlf
    call WaitMsg
    exit
main ENDP

trim_string PROC
    ; 从string_1移除选定的尾随字符
    INVOKE Str_trim, ADDR string_1, '/'
    mov edx, OFFSET msg5
    call WriteString
    mov edx, OFFSET string_1
    call WriteString
    call Crlf
    RET
trim_string ENDP

upper_case PROC
    ; 将string_1转换为大写
    mov edx, OFFSET msg0
    call WriteString
    INVOKE str_ucase, ADDR string_1
    mov edx, OFFSET string_1
    call WriteString
    call Crlf
    RET
upper_case ENDP

compare_strings PROC
    ; 比较string_1 和 string_2
    INVOKE Str_compare, ADDR string_1, ADDR string_2
    .IF ZERO?
        mov edx, OFFSET msg1
    .ELSEIF CARRY?
        mov edx, OFFSET msg2        ; string 1 is less than ...
    .ELSEIF 
        mov edx, OFFSET msg3        ; string 2 is less than ...
    .ENDIF
    call WriteString
    call Crlf
    RET
compare_strings ENDP

print_length PROC
    ; 显示string_2的长度
    mov edx, OFFSET msg4
    call WriteString
    INVOKE Str_length, ADDR string_2
    call WriteDec
    call Crlf
    RET
print_length ENDP
END
3110861-c9db47e00f52d612.png
String Library Demo.png
7). Irvine64库中字符串程序
3110861-31470c22401e4f76.png
String Procedures in the Irvine64 Library.png
  • Str_compare
; -------------------------------------------------
; Str_compare
; Compares two strings
; Receives: RSI points to the source string
; RDI points to the target string
; Returns: Sets ZF if the strings are equal
; Sets CF if source < target
; -------------------------------------------------
Str_compare PROC USES rax rdx rsi rdi
L1: 
    mov al,[rsi]
    mov dl,[rdi]
    cmp al,0        ; end of string1?
    jne L2          ; no
    cmp dl,0        ; yes: end of string2?
    jne L2          ; no
    jmp L3          ; yes, exit with ZF = 1
L2: 
    inc rsi         ; point to next
    inc rdi
    cmp al,dl       ; chars equal?
    je L1           ; yes: continue loop
    ; no: exit with flags set
L3: 
    ret
Str_compare ENDP
  • Str_copy
;------------------------------------------------------
; Str_copy
; Copies a string
; Receives: RSI points to the source string
; RDI points to the target string
; Returns: nothing
;------------------------------------------------------
Str_copy PROC USES rax rcx rsi rdi
    mov rcx,rsi     ; get length of source string
    call Str_length ; returns length in RAX
    mov rcx,rax     ; loop counter
    inc rcx         ; add 1 for null byte
    cld             ; direction = up
    rep movsb       ; copy the string
    ret
Str_copy ENDP
  • Str_length
;-------------------------------------------------------
; Str_length
; Gets the length of a string
; Receives: RCX points to the string
; Returns: length of string in RAX
;-------------------------------------------------------
Str_length PROC USES rdi
    mov rdi,rcx             ; get pointer
    mov eax,0               ; character count
L1:
    cmp BYTE PTR [rdi],0    ; end of string?
    je L2                   ; yes: quit
    inc rdi                 ; no: point to next
    inc rax                 ; add 1 to count
    jmp L1
L2: 
    ret                     ; return count in RAX
Str_length ENDP
  • 测试程序
; 测试Irvine64库字符串程序

Str_compare PROTO
Str_length PROTO
Str_copy PROTO
ExitProcess PROTO

.data
    source BYTE "AABCDEFGAABCDEFG", 0
    target BYTE 20 DUP(0)

.code
main PROC
    mov rcx, offset source
    call Str_length             ; 在EAX中返回字符串长度

    mov rsi, offset source
    mov rdi, offset target
    call Str_copy               ; 复制字符串
    call Str_compare            ; ZF = 1, 字符串相等

    ; 改变目标字符串中第一个字符,并再一次比较
    mov target, 'B'
    call str_compare            ; CF = 1, source < target
    mov ecx, 0
    call ExitProcess
main ENDP
END
3. 二维数组
1). 行和列的排序

在汇编语言中,二维数组是一维数组的高级抽象。高级语言在内存中整理选择行和列两个方法中的一个:行为主轴排序,列为主轴排序。当行为主轴排列的时候,第一行出现在内存块的开头,第一行的最后一个元素跟随者第二行第一个元素。当列为主轴排列的时候,第一列出现在内存卡的开头,第一列的最后一个元素跟随者第二列的第一个元素。x86指令集包括两个操作数类型,base-index和base-index-displacement,两者都适用于数组应用程序。

3110861-2d7eb8e630ed5ed8.png
Row-major and column-major ordering.png
2). Base-Index 操作数

Base-Index操作数将两个寄存器的值相加。方括号是必须要写的,

[base + index]

示例:

.data
    array WORD 1000h,2000h,3000h
.code
    mov ebx,OFFSET array
    mov esi,2
    mov ax,[ebx+esi] ; AX = 2000h
    mov edi,OFFSET array
    mov ecx,4
    mov ax,[edi+ecx] ; AX = 3000h
    mov ebp,OFFSET array
    mov esi,0
    mov ax,[ebp+esi] ; AX = 1000h
  • 二维数组
    当以行主要顺序访问二维数组时,行偏移保存在基址寄存器中,列偏移量在索引寄存器中。
; 三行无列的数组

tableB BYTE 10h, 20h, 30h, 40h, 50h
Rowsize = ($ - tableB)
    BYTE 60h, 70h, 80h, 90h, 0A0h
    BYTE 0B0h, 0C0h, 0D0h, 0E0h, 0F0h
; 获取数据方式
row_index = 1
column_index = 2
mov ebx,OFFSET tableB ; table offset
add ebx,RowSize * row_index ; row offset
mov esi,column_index
mov al,[ebx + esi] ; AL = 80h
3110861-725aac106cbab5c2.png
Addressing an array with a base-index operand.png
  • 计算列的和
;-----------------------------------------------------------
; calc_row_sum
; Calculates the sum of a row in a byte matrix.
; Receives: EBX = table offset, EAX = row index,
; ECX = row size, in bytes.
; Returns: EAX holds the sum.
;-----------------------------------------------------------
calc_row_sum PROC USES ebx ecx edx esi
    mul ecx                         ; row index * row size
    add ebx,eax                     ; row offset
    mov eax,0                       ; accumulator
    mov esi,0                       ; column index
L1: 
    movzx edx,BYTE PTR[ebx + esi]   ; get a byte
    add eax,edx                     ; add to accumulator
    inc esi                          ; next byte in row
    loop L1
    ret
calc_row_sum ENDP
  • 缩放因子
; 其中TYPE tableW的值为2,即缩放因子。
.data
    tableW WORD 10h, 20h, 30h, 40h, 50h
    RowsizeW = ($ - tableW)
         WORD 60h, 70h, 80h, 90h, 0A0h
         WORD 0B0h, 0C0h, 0D0h, 0E0h, 0F0h
.code
    row_index = 1
    column_index = 2
    mov ebx,OFFSET tableW           ; table offset
    add ebx,RowSizeW * row_index    ; row offset
    mov esi,column_index
    mov ax,[ebx + esi*TYPE tableW]  ; AX = 0080h
3). Base-Index-Displacement 操作数

Base-Index-Displacement 操作数联合移位,基寄存器,索引寄存器和一个可选的缩放因子产生一个有效的地址。

[base + index + displacement]
displacement[base + index]

示例:

.data
    tableD DWORD 10h, 20h, 30h, 40h, 50h
    Rowsize = ($ - tableD)
     DWORD 60h, 70h, 80h, 90h, 0A0h
     DWORD 0B0h, 0C0h, 0D0h, 0E0h, 0F0h
.code 
    mov ebx,Rowsize         ; row index
    mov esi,2               ; column index
    mov eax,tableD[ebx + esi*TYPE tableD]
3110861-318461ecb8c86599.png
Base-index-displacement example.png
4). 64位中的Base-Index操作数
; 64位下的二维数组

Crlf PROTO
WriteInt64 PROTO
ExitProcess PROTO

.data
    table QWORD 1, 2, 3, 4, 5
    RowSize = ($ - table)
        QWORD 6, 7, 8, 9, 10
        QWORD 11, 12, 13, 14, 15
.code
main PROC
    ; base-index-displacement 操作数
    mov rax, 1              ; 行索引
    mov rsi, 4              ; 列索引
    call get_tableVal       ; 返回值给RAX
    call WriteInt64         ; 显示
    call Crlf


    mov ecx, 0              ; 结束程序
    call ExitProcess
main ENDP

;---------------------------------------------------
; get_tableVal
; Returns the array value at a given row and column
; in a two-dimensional array of quadwords.
; Receives: RAX = row number, RSI = column number
; Returns: value in RAX
;---------------------------------------------------
get_tableVal PROC USES rbx
    mov rbx, RowSize
    mul rbx                 ; 结果存放在RAX中
    mov rax, table[rax + rsi * TYPE table]
    RET
get_tableVal ENDP

END
4. 搜索和排序整型数组
1). 冒泡排序

冒泡排序比较数组中的数值,开始位置为0和1,如果比较是逆序的,则将他们交换。一次循环之后,这个数组仍然是无序的,但是最大的值在索引最大的位置,通过外层循环n-1次之后,这个数组变成有序的。

3110861-aaeda04f2aa3345e.png
First pass through an array (bubble sort).png

冒泡排序的平均运行时间如下图

3110861-deded014a8dbec92.png
Average running time.png
  • 代码
;-------------------------------------------------------
; BubbleSort
; Sort an array of 32-bit signed integers in ascending
; order, using the bubble sort algorithm.
; Receives: pointer to array, array size
; Returns: nothing
;-------------------------------------------------------
BubbleSort PROC USES eax ecx esi,
    pArray:PTR DWORD,   ; pointer to array
    Count:DWORD         ; array size
    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
    jg L3               ; if [ESI] <= [ESI+4], no exchange
    xchg eax,[esi+4]    ; 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
2). 二叉树搜索

在搜索大型数组中的单个项目时,二叉树搜索算法特别有效。 它有一个重要的前提条件:数组元素必须按升序或降序排列。

3110861-5a6b9f800eb3ce62.png
Maximum Comparisons for Sequential and Binary Search.png
;--------------------------------------------------------------
; BinarySearch
; Searches an array of signed integers for a single value.
; Receives: Pointer to array, array size, search value.
; Returns: If a match is found, EAX = the array position of the
; matching element; otherwise, EAX = -1.
;--------------------------------------------------------------
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
    ; mid = (last + first) / 2
    mov eax,last
    add eax,first
    shr eax,1
    mov mid,eax
    ; EDX = values[mid]
    mov esi,mid
    shl esi,2 ; scale mid value by 4
    mov edx,[ebx+esi] ; EDX = values[mid]
    ; if ( EDX < searchval(EDI) )
    cmp edx,edi
    jge L2
    ; first = mid + 1
    mov eax,mid
    inc eax
    mov first,eax
    jmp L4
    ; else if( EDX > searchVal(EDI) )
L2: 
    cmp edx,edi ; optional
    jle L3
    ; last = mid - 1
    mov eax,mid
    dec eax
    mov last,eax
    jmp L4
    ; else return mid
L3: 
    mov eax,mid ; value found
    jmp L9 ; return (mid)
L4: 
    jmp L1 ; continue the loop
L5: 
    mov eax,-1 ; search failed
L9: 
    ret
BinarySearch ENDP
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值