8086汇编-逻辑运算和位移指令
前言:这篇笔记主要逻辑指令、位移指令和循环指令相关知识。
编程语言:
汇编语言
以下为主题内容:
逻辑指令
逻辑指令中,除 NOT 指令不影响标志位,其他逻辑指令都会影响标志位。指令执行之后会使 CF 和 OF 清 0,PF、ZF、SF反映运算结果。
NOT(取反)
指令格式:
NOT OPRD
这条指令把 OPRD 取反,然后再送回 OPRD
范例:
NOT AX
NOT VAR
AND(与)
指令格式:
AND OPRD1,OPRD2
使用逻辑 “与” 运算,将结果传送到目的操作数 OPRD1
范例:
AND DH,DH
AND AX,ES:
常见用途:
1. 保证操作数不变的情况下,让 CF 位清 0
2. 将一个字节数据的高 4 位置 0
OR(或)
指令格式:
OR OPRD1,OPRD2
使用逻辑 “或” 运算,结果送到目的操作数 OPRD1
范例:
OR AX,8080H
OR CL,AL
OR ,AX
MOV AL,41H
OR AL,20H
XOR(异或)
指令格式:
XOR OPRD1,OPRD2
常见用途:
[*]操作数和 CF 位同时置 0
[*]将一个字节数据的低 4 位取反
TEST(测试指令)
指令格式:
TEST OPRD1,OPRD2使用逻辑 “与” 运算,只影响标志位,不会改变 OPRD1。
常见用途:
[*]判断某个位是否为1
[*]判断这个值是否为0
test 指令没有改变操作数,不完全属于逻辑运算指令。
位移指令
所有位移指令如果位移多位,需要将移位位数存放在 CL 寄存器中。
SAL/SHL(算数左移指令/逻辑左移指令)
算数左移和逻辑左移的硬编码相同,没有区别。根据编译器的不同,它只会选择一种汇编指令编译。
指令格式:
SAL OPRD,m
SHL OPRD,m
[*]判断某个位是否为1
[*]乘法运算:计算4*2
SAR(算数右移指令)
指令格式:
SAR OPRD,m
操作数右移m位,同时每一栋一位,左边符号位保持不变,移出的最低位进入标志位CF。
范例:
SAR AL,1
SAR BX,CL
对于有符号数而言,算数右移一位相当于除以2
不建议用在无符号数,容易出错。
SHR(逻辑右移指令)
指令格式:
SHR OPRD,m
范例:
SHR BL,1
SHR AX,CL
对于无符号数而言,逻辑右移相当于除以2
循环位移指令
8086 有四条循环位移指令:ROL(左循环位移),ROR(右循环位移),RCL(带进位左循环位移),RCR(带进位右循环位移)。
ROL(左循环位移指令)
每移位一次,操作数左移,其最高位移入最低位,同时最高位也移入进位标志 CF
ROR(右循环位移指令)
每移位一次,操作数右移,其最低位移入最高位,同时最低位也移入进位标志 CF
RCL(带进位左循环位移指令)
每移位一次,操作数左移,其最高位移入进位标志 CF,CF 移入最低位。
RCR(带进位右循环位移指令)
每移位一次,操作数右移,其最低位移入进位标志 CF,CF 移入最高位。
转移指令
无条件转移指令
指令格式:
jmp 标号
条件转移指令分为段内转移和段间转移。
;程序名:jmp1.asm
;测试转移指令
;=============================
assume cs:code
code segment
start:
N:
mov ax,0
mov bx,0
jmp S
P:
add ax,1
S:
jmp O
inc ax
inc ax
O:
mov ax,4c00h
int 21h
code ends
end start
段内转移
段内转移的范围是 -128~127,默认使用一个字表示地址差。
使用 "标号" 进行跳转
"jmp+标号" 硬编码是 "EB+地址",默认地址是一个字大小,不足一个字会使用 90 填充。
如果确认一个字节可以表示地址差,那么可以在标号前使用 short 表示。
;程序名:jmp2.asm
;测试转移指令
;=============================
assume cs:code
code segment
start:
N:
mov ax,0
mov bx,0
jmp short S
P:
add ax,1
S:
jmp short O
inc ax
inc ax
O:
mov ax,4c00h
int 21h
code ends
end start
由此可见,在当时硬盘和内存还很小的情况下,编写程序使用 short 可以节省一些硬盘和内存空间。
使用 "存储单元" 进行跳转
;程序名:jmp3.asm
;转移指令
;=============================
assume cs:code,ds:data
data segment
addr dw 0eh
data ends
code segment
start:
mov ax,data
mov ds,ax
N:
mov ax,0
mov bx,0
jmp addr
P:
add ax,1
S:
jmp O
inc ax
inc ax
O:
mov ax,4c00h
int 21h
code ends
end start
使用存储单元跳转是直接,将内存数据传输到 IP 寄存器中。如果要这样写,需要手动计算 IP 的值,很容易出错。
使用 "$+立即数" 进行跳转
"\$" 伪指令表示 IP 寄存器的值。使用 "jmp \$+立即数" 可以跳转到相应地址。
;程序名:jmp$.asm
;转移指令
;=============================
assume cs:code,ds:data
data segment
addr dw 0eh
data ends
code segment
start:
mov ax,data
mov ds,ax
N:
mov ax,0
mov bx,0
jmp $+5
P:
add ax,1
S:
jmp O
inc ax
inc ax
O:
mov ax,4c00h
int 21h
code ends
end start
段间直接转移
段间间接转移
条件转移指令
### 循环指令
条件转移指令和无条件转移指令可以实现循环,但为了方便循环指令的实现,8086 还提供了四条循环指令。
循环指令不影响各标志位
LOOP(计数循环指令)
LOOPE/LOOPZ(等于/全零循环指令)
LOOPNE/LOOPNZ(不等于/非零循环指令)
JCXZ(跳转指令)
代码补充
;程序名:shr.asm
;功能:假设 DATA1 和 DATA2各长4位,分别存放在 AL 寄存器的低 4 位和高 4 位中,
;现在要把它们分别存放在BL寄存器和BH寄存器的低 4 位
;==========================================================================
assume cs:code,ds:data
data segment
DATA1 db 08h
DATA2 db 04h
data ends
code segment
start:
mov ax,data
mov ds,ax
;
mov ax,0
mov cl,4
;
mov al,DATA1
shl al,cl
or al,DATA2
mov bl,al
shl bx,cl
shr bl,cl
;
mov ax,4c00h
int 21h
code ends
end start
;程序名:ROL.asm
;功能:实现把AL的高4位与低4位交换。
;================================
assume cs:code
code segment
start:
xor ax,ax
mov al,37h
mov cl,4
rol al,cl
;
mov ax,4c00h
int 21h
code ends
end start
;程序名:ROR.asm
;功能:实现把AL的最低位送入BL的最低位,仍保持AL不变
;=================================================
assume cs:code
code segment
start:
xor ax,ax
xor bx,bx
mov al,29h
mov bl,28h
;
loop1:
ror bl,1
ror al,1
rcl bl,1
rol al,1
;
mov ax,4c00h
int 21h
code ends
end start
;程序名:shift.asm
;功能:设DATA1存放在AL的低4位,DATA2存放在AH的低4位,DATA3存放在SI的低4位,DATA4存放在SI的高4位。
; 现在要把这四个数据合并为16位,并存放到DX寄存器中。
assume cs:code,ds:data
data segment
DATA1 db 1
DATA2 db 2
DATA3 db 3
DATA4 db 4
data ends
code segment
start:
mov ax,data
mov ds,ax
;
mov cl,4
mov al,DATA1
mov ah,DATA2
mov bl,DATA3
mov bh,DATA4
shl bh,cl
mov si,bx
xor bx,bx
;
rol ax,cl
shr ah,cl
or ah,al
;
mov dx,si
shl dl,cl
shr dh,cl
or dl,dh
mov dh,ah
;
mov ax,4c00h
int 21h
code ends
end start
;程序名:jae.asm
;功能:比较两个无符号数的大小,较大的数存放在 AX 中,较小的数存放在 BX 中
assume cs:code,ds:data
data segment
num1 dw 24
num2 dw 35
data ends
code segment
start:
mov ax,data
mov ds,ax
;
mov ax,num1
mov bx,num2
cmp ax,bx
jae stop
xchg ax,bx
stop:
mov ax,4c00h
int 21h
code ends
end start
;程序名:jge.asm
;功能:比较两个无符号数的大小,较大的数存放在 AX 中,较小的数存放在 BX 中
assume cs:code,ds:data
data segment
num1 dw 24
num2 dw 35
data ends
code segment
start:
mov ax,data
mov ds,ax
;
mov ax,num1
mov bx,num2
cmp ax,bx
jge stop
xchg ax,bx
stop:
mov ax,4c00h
int 21h
code ends
end start
;程序名:loop.asm
;功能:十六进制数转换成ASCII输出
;=============================
assume cs:code,ds:data
data segment
val dw 0A2B4h
result db ?,?,?,?,'H',24H
data ends
code segment
start:
mov ax,data
mov ds,ax
xor si,si
mov cx,4
mov ax,val
;
loop_start:
push cx
mov cl,4
rol ax,cl
push ax
and al,0fh
cmp al,9
jnb char
add al,30h
mov result,al
jmp loop_end
;
char:
add al,37h
mov result,al
;
loop_end:
pop ax
pop cx
inc si
loop loop_start
;
mov dx,offset result
mov ah,9
int 21h
;
mov ax,4c00h
int 21h
code ends
end start
;程序名:loop1.asm
;功能:把从偏移 1000H 开始的 512 个字节的数据复制到从偏移 3000H 开始的缓冲区中
assume cs:code
code segment
start:
mov si,1000H
mov di,3000H
mov cx,512
next:
mov al,
inc si
mov ,al
inc di
loop next
;
mov ax,4c00h
int 21h
code ends
end start
;程序名:loopz.asm
;功能:查找第一个非 'A' 字符,找到 BX 保存该字符的偏移地址,找不到 BX=0FFFFh
;=========================================================================
assume cs:code,ds:data
data segment
val db 'AAAAAAAA',24h
data ends
code segment
start:
mov ax,data
mov ds,ax
mov cx,8
xor si,si
;
loop_start:
mov al,val
inc si
cmp al,41h
;loopz:cx不为0 and zf 标志位为 1 才会继续循环,只要其中一个条件不成立就会跳出循环
loopz loop_start
cmp byte ptr ,24H
jz no_find
mov bx,si
jmp stop
no_find:
mov bx,-1
stop:
mov ax,4c00h
int 21h
code ends
end start
;程序名:loopz2.asm
;功能:查找第一个非 'A' 字符,找到 BX 保存该字符的偏移地址,找不到 BX=0FFFFh
;=========================================================================
assume cs:code,ds:data
data segment
val db 'AAABABAA'
len = $ - val
data ends
code segment
start:
mov ax,data
mov ds,ax
mov cx,len
xor si,si
;
loop_start:
mov al,val
inc si
cmp al,41h
;循环判断字符串中的字符 'A'
loopz loop_start
;如果 CX = 0,说明没有找到字符 'A'
cmp cx,0
jz no_find
mov bx,si
jmp stop
no_find:
mov bx,-1
stop:
mov ax,4c00h
int 21h
code ends
end start
;程序名:loopz3.asm
;功能:查找第一个非 'A' 字符,找到 BX 保存该字符的偏移地址,找不到 BX=0FFFFh
;=========================================================================
assume cs:code,ds:data
data segment
val db 'AAABABAA'
len = $ - val
data ends
code segment
start:
mov ax,data
mov ds,ax
mov cx,len
xor si,si
loop_start:
mov al,val
inc si
cmp al,41h
loopz loop_start
;正常循环结束后的 CX = 0
jz no_find
mov bx,si
jmp stop
no_find:
mov bx,-1
stop:
mov ax,4c00h
int 21h
code ends
end start
感谢楼主 楼主的帖子不错,多发点~ 感谢楼主 谢谢分享 感谢楼主 感谢楼主 谢谢分享 谢谢分享 有楼主这样的热心人真好