1. Shift 和 Rotate 指令
位移意味着在操作数内部按位左/右移动,其影响着OF和CF标志位。
1). 逻辑移动和算术移动
- 逻辑移动
将新创建的字节位填充0,
- 算术移动
新创建的字节位填充原位置上的数据。
2). SHL 指令
SHL指令表示在目标操作数上逻辑左移,低位填充0。
- 格式
; 第一个是目标操作数,第二个是移动的位数
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
3). SHR 指令
SHR 指令表示在目标操作数上逻辑右移,高位用0替换。
示例:
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
4). SAL 和 SAR 指令
- SAL
SAL 指令与SHL指令相同。SAL指令的每一位向高位移动,低位补0.
- SAR
SAR 指令移动后新的位置上填充原位置上的数字。
示例:
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 指令指循环移动,即移出的一位补充到先增加的那个位置上。
示例:
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标志位和最高位。
示例:
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标志位和最低位。
示例:
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标志位。
示例:
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
- 示例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代表年。
示例:
; 获取天
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
; 示例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
4). DIV 指令
- 格式
DIV reg/mem8
DIV reg/mem16
DIV reg/mem32
- 示例
; 示例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
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
5. ASCII 和未压缩的十进制算法
- 四个处理指令
- ASCII 十进制 和 未解压十进制
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
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
2). DAS
mov bl,48h
mov al,85h
sub al,bl ; AL = 3Dh
das ; AL = 37h (adjusted result)