10 逻辑运算和移位指令
1 逻辑运算
| 指令 | and | or | xor | not | test | 
| C 算符 | & | \| | ^ | ~ |  | 
| 解释 |  |  |  |  | 不保留结果的 and | 
1.1 test 运算
Note
- 相当于进行一个 AND运算,结果不会保留但会影响 flag
- test与- and的关系相当于- cmp和- sub的关系
 
| test | 
|---|
|  | mov ax, 1234h
test ax, 8000h  ; ZF = 0, AX = 1234h
 | 
2 移位运算
| 指令 | shl | shr | rol | ror | sal | sar | rcl | rcr | 
| C 算符 | << | >> | _rotl() | _rotr() |  |  |  |  | 
| 解释 |  |  | 循环左移 | 循环右移 | 算数左移 | 算数右移 | 带进位的逻辑左移 | 带进位的逻辑右移 | 
2.1 循环移位指令 rol, ror
移位与 CF 的关系
Warning
CF 里保留的一定是最后移出去的一位,不论是什么移位指令
 
| shift - CF | 
|---|
|  | mov ah, 0EFh  ; 1110 1111, CF = ?
ror ah, 1  ;    1111 0111, CF = 1
 | 
 
| printf() | 
|---|
|  | .386
data segment use16
abc dd 2147483647
data ends
code segment use16
assume cs:code, ds:data
main:
    mov ax, seg abc
    mov ds, ax
    mov eax, abc  ; 复习:编译后编程 mov eax, ds:[0],所以前面要先赋值
    mov cx, 8
again:
    rol eax, 4
    push eax  ; 为了暂时保护 eax 的值,保存到栈中
    and eax, 0Fh  ; 这时 and 运算清除了 eax 前面部分的内容
    cmp al, 10
    jb is_digit
is_alpha:  // 16进制中的字母也可以打印
    sub al, 10
    add al, 'A'
    jmp finish_4bits
is_digit:
    add al, '0'
finish_4bits:
    mov ah, 2
    mov dl, al
    int 21h
    pop eax  ; 表示从栈中恢复上次 push 时 eax 的值
    sub cx, 1
    jnz again  ; jump if not zero
    mov ah, 4Ch
    int 21h
code ends
end main
 | 
使用 rol 指令,将 32-bit 数输出成 16 进制的格式
2.2 算数移位指令 sal, sar
- sal算数左移,其实等价于- shl
- sar算数右移,不等于- shr,因为有符号问题
| arithmetic shift | 
|---|
|  | mov ah, 0FEh  ; 1111 1110 = -2
sar ah, 1  ;    1111 1111 = -1
mov ah, 0FEh  ; 1111 1110 = 254
shr ah, 1  ;    0111 1111 = 124
 | 
2.3 进位循环位移指令 rcl, rcr
- rcl带进位循环左移
- rcr带进位循环右移
- 每次移入 CF,移出的到CF
| shift 1234ABCDh left 3 bit without 32-bit reg | 
|---|
|  | ; method 1
mov ax, 0ABCDh
mov dx, 1234h
shl dx, 3
mov bx, ax
shl ax, 3
shr bx, 13
or dx, bx
; method 2 with rcl
mov ax, 0ABCDh
mov dx, 1234h
mov cx, 3
next:
    shl ax, 1  ; 移到 CF
    rcl dx, 1  ; CF 会补 DX 右侧的空洞
    dec cx
    jnq next
 |