汇编开发(五):算法

1. Shift 和 Rotate 指令

位移意味着在操作数内部按位左/右移动,其影响着OF和CF标志位。

3110861-d0fe257aa420bcc6.png
Shift and Rotate Instructions.png
1). 逻辑移动和算术移动
  • 逻辑移动
    将新创建的字节位填充0,
3110861-990dfeef7e5c41a1.png
Logical shift.png
  • 算术移动
    新创建的字节位填充原位置上的数据。
3110861-f38ab9330c09461f.png
Arithmetic shift.png
2). SHL 指令

SHL指令表示在目标操作数上逻辑左移,低位填充0。

3110861-f61a7d1dfce27d44.png
SHL.png
  • 格式
; 第一个是目标操作数,第二个是移动的位数
SHL destination,count

SHL reg,imm8
SHL mem,imm8
SHL reg,CL
SHL mem,CL

x86 处理器支持imm8的范围在整型0—255之间,另外,CL寄存器也可以包含移动的位数。此格式同样应用在SHR, SAL, SAR, ROR, ROL,
RCR 和RCL 指令。

示例:

mov bl,8Fh      ; BL = 10001111b
shl bl,1        ; CF = 1, BL = 00011110b

mov al,10000000b
shl al,2        ; CF = 0, AL = 00000000b
  • 按位乘
    左移n位相当于操作数乘以2^n。
mov dl,5
shl dl,1
3110861-a1111eaf46e865d4.png
Bitwise Multiplication.png
3). SHR 指令

SHR 指令表示在目标操作数上逻辑右移,高位用0替换。

3110861-fd8c8604fa46431f.png
SHR.png

示例:

mov al,0D0h     ; AL = 11010000b
shr al,1        ; AL = 01101000b, CF = 0

mov al,00000010b
shr al,2        ; AL = 00000000b, CF = 1
  • 按位除
    右移一个无符号整数n位相当于操作数除以2^n。
mov dl,32
shr dl,1
3110861-5c6c9d28907c92f9.png
Bitwise Division.png
4). SAL 和 SAR 指令
  • SAL
    SAL 指令与SHL指令相同。SAL指令的每一位向高位移动,低位补0.
3110861-25dec89ddc4e2939.png
SAL.png
  • SAR
    SAR 指令移动后新的位置上填充原位置上的数字。
3110861-61eeb9bc0488792f.png
SAR.png

示例:

mov al,0F0h     ; AL = 11110000b (-16)
sar al,1        ; AL = 11111000b (-8), CF = 0
  • 符号除
    有符号的操作数除以2,可以使用SAR指令。
mov dl,-128     ; DL = 10000000b
sar dl,3        ; DL = 11110000b
  • AX符号扩展到EAX
    AX包含一个有符号的整型,并将它扩展到EAX寄存器中。
mov ax,-128     ; EAX = ????FF80h
shl eax,16      ; EAX = FF800000h
sar eax,16      ; EAX = FFFFFF80h
5). ROL 指令

ROL 指令指循环移动,即移出的一位补充到先增加的那个位置上。

3110861-35092d9ee22c51b1.png
SHL.png

示例:

mov al,40h      ; AL = 01000000b
rol al,1        ; AL = 10000000b, CF = 0
rol al,1        ; AL = 00000001b, CF = 1
rol al,1        ; AL = 00000010b, CF = 0
  • 多次旋转
    当循环位数大于1的时候,进位标志与最后一位相同
mov al,00100000b
rol al,3            ; CF = 1, AL = 00000001b
  • 交换比特组
    可以使用ROL交换高位和低位的字节。
mov al,26h
rol al,4        ; AL = 62h
6). ROR 指令

ROR 指令 每一位右移,并复制最低位到CF标志位和最高位。

3110861-a8f427c96a1da724.png
ROR.png

示例:

mov al,01h      ; AL = 00000001b
ror al,1        ; AL = 10000000b, CF = 1
ror al,1        ; AL = 01000000b, CF = 0
  • 多次旋转
    当使用旋转位数大于1时,CF标志位与最后一次移动的位数值相同。
mov al,00000100b
ror al,3            ; AL = 10000000b, CF = 1
7). RCL 和 RCR 指令

RCL 指令向左移动每一位,复制到CF标志位和最低位。

3110861-678963d3f5a40929.png
RCL.png

示例:

clc         ; CF = 0 清空CF标志位
mov bl,88h  ; CF,BL = 0 10001000b
rcl bl,1    ; CF,BL = 1 00010000b
rcl bl,1    ; CF,BL = 0 00100001b
  • 从进位标志中恢复
    RCL 可以恢复移动之前的CF标志位。
.data
    testval BYTE 01101010b
.code
    shr testval,1   ; shift LSB into Carry flag
    jc exit         ; exit if Carry flag set
    rcl testval,1   ; else restore the number
  • RCR 指令
    RCR 指令向右移动每一位,复制CF标志位到最高位,将最低位复制到CF标志位。
3110861-1ed0e2fc34dbd545.png
RCR.png

示例:

stc         ; CF = 1 设置CF标志位为1
mov ah,10h  ; AH, CF = 00010000 1
rcr ah,1    ; AH, CF = 10001000 0
8). 符号溢出

移动或者旋转一个有符号的整型时,符号位溢出后,OF标志位被设置。

mov al,+127 ; AL = 01111111b
rol al,1    ; OF = 1, AL = 11111110b
9). SHLD / SHRD 指令

SHLD/SHRD 指令向左/右移动一个目标操作数指定的数字位数,新添加的位数由源操作数中的高/低位填充。格式如下:

SHLD/SHRD dest, source, count

SHLD/SHRD reg16,reg16,CL/imm8
SHLD/SHRD mem16,reg16,CL/imm8
SHLD/SHRD reg32,reg32,CL/imm8
SHLD/SHRD mem32,reg32,CL/imm8
3110861-5c22c78c41067983.png
SHLD.png
3110861-9918ad4e870b5760.png
SHRD.png
  • 示例1:
.data
    wval WORD 9BA6h
.code
    mov ax,0AC36h
    shld wval,ax,4      ; wval = BA6Ah
  • 示例2:
mov ax,234Bh
mov dx,7654h
shrd ax,dx,4        ; ax = 4234h
2. 移动和旋转应用
1). 转换多个DWORD
; 数组元素移位

INCLUDE Irvine32.inc

.data
    ArraySize = 3
    array BYTE ArraySize DUP(99h)

.code
main PROC
    mov esi, 0
    shr array[esi + 2], 1       ; 最高字节
    rcr array[esi + 1], 1       ; 中间字节
    rcr array[esi], 1           ; 低字节
    
    call Crlf
    call WaitMsg
    exit
main ENDP

END
2). 二进制乘法

将某个数分解之后成为2的n次方,并将乘数依次与之相乘,最后求和。例如:36可以分为25+22, 则10036 = 10025+100*22。

mov eax,100
mov ebx,eax
shl eax,5   ; multiply by 25
shl ebx,2   ; multiply by 22
add eax,ebx ; add the products
3). 显示二进制位

共同的代码程序将二进制整数转换为ASCII码字符串,并将ASCII码显示。

;---------------------------------------------------------
;
; Converts 32-bit binary integer to ASCII binary.
; Receives: EAX = binary integer, ESI points to buffer
; Returns: buffer filled with ASCII binary digits
;---------------------------------------------------------
BinToAsc PROC USES ecx, esi
    mov ecx,32              ; number of bits in EAX
L1: 
    shl eax,1               ; shift high bit into Carry flag
    mov BYTE PTR [esi],'0'  ; choose 0 as default digit
    jnc L2                  ; if no Carry, jump to L2
    mov BYTE PTR [esi],'1'  ; else move 1 to buffer
L2: 
    inc esi                 ; next buffer position
    loop L1                 ; shift another bit to left
    RET
BinToAsc ENDP
4). 提取文件日期

应用经常需要提取位串用于获取时间。在MS-DOS中日期存储在DX中,其中0—4位代表天,5—8位代表月,9—15代表年。

3110861-204ced80b807bd8d.png
Extracting File Date Fields.png

示例:

; 获取天
mov al,dl           ; make a copy of DL
and al,00011111b    ; clear bits 5-7
mov day,al          ; save in day

; 获取月
mov ax,dx           ; make a copy of DX
shr ax,5            ; shift right 5 bits
and al,00001111b    ; clear bits 4-7
mov month,al        ; save in month

; 获取年
mov al,dh           ; make a copy of DH
shr al,1            ; shift right one position
mov ah,0            ; clear AH to zeros
add ax,1980         ; year is relative to 1980
mov year,ax         ; save in year
3. MUL 和 DIV 指令

在32位模式中,MUL和DIV 可以作用于32位,16位,8位操作数。
在64为模式中,MUL和DIV指令作用于无符号整型中,IMUL和IDIV指令作用于有符号整型中。

1). MUL 指令
  • 格式
MUL reg/mem8
MUL reg/mem16
MUL reg/mem32
3110861-3a0b1fab64a8d0bf.png
MUL Operands.png
; 示例1
mov al,5h
mov bl,10h
mul bl      ; AX = 0050h, CF = 0

; 示例2
.data
val1 WORD 2000h
val2 WORD 0100h
.code
mov ax,val1     ; AX = 2000h
mul val2        ; DX:AX = 00200000h, CF = 1

; 示例3
mov eax,12345h
mov ebx,1000h
mul ebx         ; EDX:EAX = 0000000012345000h, CF = 0
  • 64位模式中的MUL
.data
    multiplier QWORD 10h
.code
    mov rax,0AABBBBCCCCDDDDh
    mul multiplier          ; RDX:RAX = 00000000000000000AABBBBCCCCDDDD0h
2). IMUL 指令

IMUL 指令表示有符号整数的乘法。

  • 单操作数格式
IMUL reg/mem8   ; AX = AL * reg/mem8
IMUL reg/mem16  ; DX:AX = AX * reg/mem16
IMUL reg/mem32  ; EDX:EAX = EAX * reg/mem32
  • 双操作数格式
IMUL reg16,reg/mem16
IMUL reg16,imm8
IMUL reg16,imm16
IMUL reg32,reg/mem32
IMUL reg32,imm8
IMUL reg32,imm32
  • 三操作数格式
IMUL reg16,reg/mem16,imm8
IMUL reg16,reg/mem16,imm16
IMUL reg32,reg/mem32,imm8
IMUL reg32,reg/mem32,imm32

如果符号位丢失,则需要设置OF和CF标志位。

  • 64位模式下的IMUL
    64位模式下,可以使用64位模式下MUL指令。在双操作数格式下,一个64位寄存器或者内存数乘以RDX,产生一个128位有符号扩展的RDX:RAX。64位模式下三操作数格式也是可用的。
; 双操作数
mov rax,-4
mov rbx,4
imul rb     ; RDX = 0FFFFFFFFFFFFFFFFh, RAX = -16

; 三操作数
.data
    multiplicand QWORD -16
.code
    imul rax, multiplicand, 4   ; RAX = FFFFFFFFFFFFFFC0 (-64)

示例:

; 单操作数
mov al,48
mov bl,4
imul bl     ; AX = 00C0h, OF = 1

mov al,-4
mov bl,4
imul bl     ; AX = FFF0h, OF = 0

; 双操作数
.data
    word1 SWORD 4
    dword1 SDWORD 4
.code
    mov ax,-16      ; AX = -16
    mov bx,2        ; BX = 2
    imul bx,ax      ; BX = -32
    imul bx,2       ; BX = -64
    imul bx,word1   ; BX = -256
    mov eax,-16     ; EAX = -16
    mov ebx,2       ; EBX = 2
    imul ebx,eax    ; EBX = -32
    imul ebx,2      ; EBX = -64
    imul ebx,dword1 ; EBX = -256

    mov ax,-32000
    imul ax,2 ; OF = 1  
    
; 三操作数
.data
    word1 SWORD 4
    dword1 SDWORD 4
.code
    imul bx,word1,-16   ; BX = word1 * -16
    imul ebx,dword1,-16 ; EBX = dword1 * -16
    imul ebx,dword1,-2000000000 ; signed overflow!
3). 比较MUL/IMUL和移位的运行时间
; 比较MUL/IMUL和移位的运行时间

INCLUDE Irvine32.inc

.data
    LOOP_COUNT = 0FFFFFFFFh
    intval DWORD 5
    startTime DWORD ?
    shiftTime DWORD ?
    mulTime DWORD ?

.code
main PROC
    call GetMseconds    ; get start time
    mov startTime,eax
    mov eax,intval      ; multiply now
    call mult_by_shifting
    call GetMseconds    ; get stop time
    sub eax,startTime
    call WriteDec       ; display elapsed time

    call Crlf

    call GetMseconds    ; get start time
    mov startTime,eax
    mov eax,intval      ; multiply now
    call mult_by_MUL
    call GetMseconds    ; get stop time
    sub eax,startTime
    call WriteDec       ; display elapsed time

    call Crlf
    call WaitMsg
    exit
main ENDP

;----------------------------------
; 使用SHL移位计算EAX*36
;----------------------------------
mult_by_shifting PROC USES eax
    mov ecx, LOOP_COUNT
L1:
    mov ebx, eax
    shl eax, 5
    shl eax, 2
    add eax, ebx
    LOOP L1
    RET
mult_by_shifting ENDP

;----------------------------------
; 使用乘法计算EAX*36
;----------------------------------
mult_by_MUL PROC USES eax
    mov ecx, LOOP_COUNT
L1:
    mov ebx, 36
    mul ebx
    LOOP L1
    RET
mult_by_MUL ENDP

END
3110861-eff34c21fe40bc9c.png
效果.png
4). DIV 指令
  • 格式
DIV reg/mem8
DIV reg/mem16
DIV reg/mem32
3110861-5907994c38ab8cff.png
被除数、除数、商和余数关系.png
  • 示例
; 示例1
mov ax,0083h    ; dividend
mov bl,2        ; divisor
div bl          ; AL = 41h, AH = 01h

; 示例2
mov dx,0        ; clear dividend, high
mov ax,8003h    ; dividend, low
mov cx,100h     ; divisor
div cx          ; AX = 0080h, DX = 0003h
5). 带符号整数除法

有符号整数除法与无符号除法几乎相同,但有一个重要区别:除法必须在除法发生之前进行符号扩展。

.data
    wordVal SWORD -101  ; 009Bh
.code
    mov eax,0           ; EAX = 00000000h
    mov ax,wordVal      ; EAX = 0000009Bh (+155)
    cwd                 ; EAX = FFFFFF9Bh (-101)
    mov bx,2            ; EBX is the divisor
    idiv bx             ; divide EAX by BX
  • 符号扩展指令 (CBW, CWD, CDQ)
    CBW 指令(BYTE -> WORD)扩展符号位从AL到AH。CWD(WORD -> DWORD)扩展符号位AX到DX。CDQ(DWORD -> QWORD)口占EAX到EDX。
; CBW 使用
.data
    byteVal SBYTE -101  ; 9Bh
.code
    mov al,byteVal      ; AL = 9Bh
    cbw                 ; AX = FF9Bh

; CWD 使用
.data
    wordVal SWORD -101  ; FF9Bh
.code
    mov ax,wordVal      ; AX = FF9Bh
    cwd

; CDQ 使用
.data
    dwordVal SDWORD -101    ; FFFFFF9Bh
.code
    mov eax,dwordVal
    cdq
  • IDIV 指令
    IDIV 指令表示有符号除法,使用和DIV相同。
.data
    byteVal SBYTE -48   ; D0 hexadecimal
.code
    mov al,byteVal      ; lower half of dividend
    cbw                 ; extend AL into AH
    mov bl,+5           ; divisor
    idiv bl             ; AL = -9, AH = -3
  • 除法溢出
mov ax,1000h
mov bl,10h
div bl      ; AL cannot hold 100h

建议:使用32位除数和64位被除数来降低除法溢出条件的概率。

6). 实现除法/乘法的算术表达式
; 示例表达式1: var4 = (var1 + var2) * var3
    mov eax,var1
    add eax,var2
    mul var3        ; EAX = EAX * var3
    jc tooBig       ; unsigned overflow?
    mov var4,eax
    jmp next
tooBig:         ; display error message

; 示例表达式2: var4 = (var1 * 5) / (var2 - 3)
    mov eax,var1    ; left side
    mov ebx,5
    mul ebx         ; EDX:EAX = product
    mov ebx,var2    ; right side
    sub ebx,3
    div ebx         ; final division
    mov var4,eax
4. 扩展加和减

扩展加和减是添加和减去几乎无限大小的数字的技术。

1). ADC 指令

ADC指令将源操作数和CF标志的内容添加到目标操作数。

  • 格式
ADC reg,reg
ADC mem,reg
ADC reg,mem
ADC mem,imm
ADC reg,imm

示例:

mov dl,0
mov al,0FFh
add al,0FFh     ; AL = FEh
adc dl,0        ; DL/AL = 01FEh
3110861-16194192db9938cd.png
效果.png
2). 扩展加示例

将数组中的数据一一对应相加。

; 扩展加法示例:数组元素对应相加

INCLUDE Irvine32.inc

.data
    op1 BYTE 34h,12h,98h,74h,06h,0A4h,0B2h,0A2h
    op2 BYTE 02h,45h,23h,00h,00h,87h,10h,80h
    sum BYTE 9 dup(0)

.code
main PROC
    mov esi, OFFSET op1
    mov edi, OFFSET op2
    mov ebx, OFFSET sum
    mov ecx, LENGTHOF op1

    call Extended_Add

    ; 显示和
    mov esi, OFFSET sum
    mov ecx, LENGTHOF sum
    call Display_Sum
    
    call Crlf
    call WaitMsg
    exit
main ENDP

;-------------------------------------------------
; 计算两个字节数组之和
; Receives: ESI和EDI指向两个整型,EBX指向一个存储和的
;           变量,ECX循环计数
; Returns: 无
;-------------------------------------------------
Extended_Add PROC
    pushad
    clc

L1: 
    mov al, [esi]       ; 获取第一个整数
    adc al, [edi]       ; 添加第二个整数
    pushfd              ; 保存CF标记位
    mov [ebx], al       ; 存储和
    add esi, 1          ; 指针指向下一个数
    add edi, 1
    add ebx, 1
    popfd               ; 恢复CF
    LOOP L1             ; 循环

    mov byte ptr [ebx], 0; 清除sum的高位
    adc byte ptr [ebx], 0; 添加CF标记位
    popad
    RET
Extended_Add ENDP

Display_Sum PROC
    pushad
    ; point to the last array element
    add esi,ecx
    sub esi,TYPE BYTE
    mov ebx,TYPE BYTE
L1: 
    mov al,[esi]        ; get an array byte
    call WriteHexB      ; display it
    sub esi,TYPE BYTE   ; point to previous byte
    call Crlf
    loop L1
    popad
    ret
Display_Sum ENDP

END

结果:0122C32B0674BB5736, 数组对应元素的和,在结果中从低位到高位显示,最高位为符号位。

3). SBB 指令

SBB指令表示从目的操作数中减去源操作数和CF标志位。

mov edx,7 ; upper half
mov eax,1 ; lower half
sub eax,2 ; subtract 2
sbb edx,0 ; subtract upper half
3110861-0f7db0a4d350bddf.png
Subtracting from a 64-bit integer using SBB.png
5. ASCII 和未压缩的十进制算法
  • 四个处理指令
3110861-139fe78d61914854.png
deal with ASCII addition, subtraction, multiplication, and division.png
  • ASCII 十进制 和 未解压十进制
3110861-50d72e5f8be7097a.png
All values are in hexadecimal.png
1). AAA 指令
mov ah,0
mov al,'8'      ; AX = 0038h
add al,'2'      ; AX = 006Ah
aaa             ; AX = 0100h (ASCII adjust result)
or ax,3030h     ; AX = 3130h = '10' (convert to ASCII)
  • 使用AAA进行多字节相加
; ASCII 加法
; 字符串中ASCII算法
INCLUDE Irvine32.inc

DECIMAL_OFFSET = 5          ; 从右数字符串偏移
.data
    decimal_one BYTE "100123456789765"  ; 1001234567.89765
    decimal_two BYTE "900402076502015"  ; 9004020765.02015
    sum BYTE (SIZEOF decimal_one + 1) DUP(0), 0

.code
main PROC
    ; 开始从最后一个数字的位置
    mov esi, SIZEOF decimal_one - 1
    mov edi, SIZEOF decimal_one
    mov ecx, SIZEOF decimal_one
    mov bh, 0                       ; 设置CF为0
L1:
    mov ah, 0                       ; 加之前清空AH
    mov al, decimal_one[esi]        ; 获取当前位置的数字
    add al, bh                      ; 添加之前的进位
    aaa                             ; 调整总和
    mov bh, ah                      ; 存储CF标志位
    or bh, 30h                      ; 转换ASCII
    add al, decimal_two[esi]        ; 添加第二个数
    aaa                             ; 调整总和
    or bh, ah                       ; 或CF标志位
    or bh, 30h                      ; 转换为ASCII码
    or al, 30h                      ; 转换al为ASCII
    mov sum[edi], al                ; 存储和
    dec esi                         ; 移动到下一位
    dec edi
    LOOP L1
    mov sum[edi], bh                ; 存储进位
    
    ; 显示总和字符串
    mov edx, OFFSET sum
    call WriteString

    call Crlf
    call WaitMsg
    exit
main ENDP

END
3110861-ab3c677df42389ca.png
效果.png
2). AAS 指令
.data
    val1 BYTE '8'
    val2 BYTE '9'
.code
    mov ah,0
    mov al,val1     ; AX = 0038h
    sub al,val2     ; AX = 00FFh
    aas             ; AX = FF09h
    pushf           ; save the Carry flag
    or al,30h       ; AX = FF39h
    popf            ; restore the Carry flag

此处8 - 9 之后,AH减1变为FFh, AL变为39h, 可理解为18 - 9,即个位为9,向前借了一位。

3). AAM 指令
.data
    AscVal BYTE 05h,06h
.code
    mov bl,ascVal       ; first operand
    mov al,[ascVal+1]   ; second operand
    mul bl              ; AX = 001Eh
    aam                 ; AX = 0300h , or 3030h
4). AAD 指令
.data
    quotient BYTE ?
    remainder BYTE ?
.code
    mov ax,0307h    ; dividend
    aad             ; AX = 0025h, or 3030h
    mov bl,5        ; divisor
    div bl          ; AX = 0207h
    mov quotient,al
    mov remainder,ah
6. 压缩十进制算术

涉及到两个指令:DAA (decimal adjust after addition) 与 DAS (decimal adjust after subtraction)。压缩十进制算术中无乘法和相关指令。

1). DAA
; 压缩加法
; 压缩十进制加法
INCLUDE Irvine32.inc

.data
    packed_1 WORD 4536h
    packed_2 WORD 7207h
    sum DWORD ?

.code
main PROC
    ; 初始化总和和序号
    mov sum, 0
    mov esi, 0

    ; 添加低字节
    mov al, BYTE PTR packed_1[esi]
    add al, BYTE PTR packed_2[esi]
    daa
    mov BYTE PTR sum[esi], al

    ; 添加高字节,包含CF标志位
    inc esi
    mov al, BYTE PTR packed_1[esi]
    adc al, BYTE PTR packed_2[esi]
    daa
    mov BYTE PTR sum[esi], al
    ; 添加CF标志位
    inc esi
    mov al, 0
    adc al, 0
    mov BYTE PTR sum[esi], al

    ; 十六进制格式显示
    mov eax, sum
    call WriteHex

    call Crlf
    call WaitMsg
    exit
main ENDP

END
3110861-99fbdc47aa1c23c2.png
AddPacked.png
2). DAS
mov bl,48h
mov al,85h
sub al,bl   ; AL = 3Dh
das         ; AL = 37h (adjusted result)
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值