一般情况下程序是按顺序逐条执行的 而在实际中 我们常需要改变程序的执行流程
- 可以控制CPU执行内存中某处代码的指令
- 可以修改ip 或者同时修改cs和ip的值
分类
- 转移行为
段内转移:只修改ip的值
段间转移:同时修改cs和ip的值 - 根据指令对ip修改范围的不同
段内短偏移 ip-128~127
段内近偏移 ip-32768~32767 - 根据转移指令
无条件转移指令jmp
条件转移指令jcxz
循环指令loop
offset标号
获取标号处的偏移地址
assume cs:code
code segment
start:mov ax,offset start
s:mov ax,offset s
code ends
end start
C++因为start位于代码段开始的地方 所以利用offset获取标号start的偏移就是0 而s位于start之后 mov ax,0 占三个字节 所以s获取的偏移是3即mov ax,3
练习
如有以下程序段 添加两条指令 使该程序在运行中将s处的一条指令复制到S0处
即s->s0
assume cs:code
code segment
s:mov ax,bx
mov si,offset s
mov di,offset s0
__________
__________
s0:nop
nop
code ends
end
C++首先我们既然要复制数据 肯定要确定二者数据的地址 s - cs:offset s s0 - cs:offset s0
还需要明确的是我们需要在源地址复制的字节数是两个字节即一个字
所以上述空线代码为mov ax,cs:[si] mov cs:[di],ax
jmp指令
可以只修改ip也可以同时修改cs和ip的值
段内(short near) 段间
常见指令中的立即数均在机器指令中有体现
示例代码
assume cs:code
code segment
start: mov ax,0
jmp short s
add ax,1
s:inc ax
code ends
end start
C++通过dosbox载入这个程序会发现 jmp short包含的是跳转指令的相对位置而不是转移的目标地址
- (ip) = 3 cs:ip指向EB 05
- 读取指令EB 05进入指令缓存器
- (ip) = (ip)+所读取指令的长度 = (ip)+2 = 0005 cs:ip指向add ax,1
- cpu执行指令缓冲器中的指令
- 指令EB 05执行后(IP) = (ip)+05 = 000AH,cs:ip指向including ax
短转移
jmp short 标号
(ip) = (ip)+8位位移 8位位移 = 标号处 - jmp指令后一个字节的地址
8位位移的范围为-128~127 用补码表示
近转移
jmp near ptr 标号
(ip) = (ip)+16位位移 16位位移 = 标号处 - jmp指令后一个字节的地址
16位位移的范围为-32769~32767 用补码表示
远转移和近转移的区别
远转移已经指明了目的地址
近转移指明了相对于当前ip的转移位移,而不是目的地址
转移地址在寄存器中的jmp指令 jmp16位寄存器
assume cs:code
code segment
start:mov ax,0
mov bx,ax
jmp bx
mov ax,0123h
code ends
end
C++转移地址没有体现在机器码中 但是由bx自由确定
转移地址在内存中的jmp指令
jmp word ptr 内存单元地址
段内转移
从内存单元地址处开始存放着一个字 是转移地址的偏移地址
mov ax,0123h
mov ds:[0],ax
jmp word ptr ds:[0]
或者可以
mov ax,0123h
mov [bx],ax
jmp word ptr [bx]
C++执行后(ip) = 0123H
jmp dword ptr 内存单元地址
段间转移
从内存单元地址处存放着两个字 高地址存放目标段地址 低地址存放转移目标地址的偏移地址
mov ax,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0]
C++转移小结
指令格式
jmp 标号 -段间转移(远转移):jmp far ptr 标号
- 段内短转移:jmp short 标号 ;8位的位移
- 段内近转移:jmp near ptr 标号 ;16位的位移
jmp 寄存器 -jmp bx ;等价于段内近转移,也是16位的位移
jmp 内存单元(用地址表示的) -jmp word ptr 内存单元地址 ;段内转移
-jmp dword ptr 内存单元地址 ;段间转移
_在源程序中,不能直接使用“jmp 2000:0100"这样的转移指令来实现段间转移,这种方式在debug模式中使用的汇编指令,在源程序中写,编译器并不识别(编译报错)_
其他转移指令
jcxz指令
如果cx = 0 则转移到标号位置执行
当cx≠0 程序向下执行 不进行跳转 当(cx) = 0 (ip) = (ip) + 8位位移
和jmp short 标号很一样 偏移是相对于ip的偏移
8位位移 = 标号处地址-jcxz指令的第一个字节的地址
范围是-128~127 用补码表示
jcxz是有条件转移指令
所有的有条件转移指令都是短转移
对ip的修改范围都为-128~127
在对应的机器码中包含转移的位移 而不是目的地址
assume cs:code
code segment
start:mov ax,2000H
mov ds,ax
mov bx,0
s:mov cx,[bx]
jcxz ok//如果cx = 0则可以跳转 不为0则什么都不做
inc bx
inc bx
jmp short s
ok:mov dx,bx
mov ax,4c00H
int 21H
code ends
end
C++04 = 10 - 0C
loop指令
(cx) = (cx)-1
当(cx)≠0时 则转移到标号处执行
如果(cx)≠0时,(ip) = (ip) + 8位位移 8位位移为负数表示向前执行 正数代表向后执行
当(cx) =0时 程序向下执行
assume cs:code
code segment
start:mov cx,6h
mov ax,10h
s:add ax,ax
loop s
mov ax,4c00h
int 21h
code ends
end
C++loop也是短转移 因此偏移是相对偏移 FC = 6-A
根据位移进行相对转移的意义
如果短转移指令是固定的地址,对偏移有了严格的限制 当机器码中包含的是相对偏移无论指令的实际地址是多少 loop指令的相对偏移是不变的
call和ret指令
cmp和条件转移指令
用jxxx系列指令和cmp指令配合实现高级语言中的if功能
例1 如果(ah) = (bh) 则(ah) = (ah)+(ah)否则(ah) = (ah)+(bh)
cmp ah,bh
je s
add ah,bh
jmp short ok
s:add ah,ah
ok:ret
C++条件转移指令
可以根据某种条件 决定是否转移程序执行流程 转移 = 修改ip
检测转移条件通过 标志寄存器 cmp可以修改标志寄存器
jxxx转移指令前面可以有cmp也可以没有cmp
编程思路:初始设置(ax) = 0 然后就循环依次比较每个字节的值 找到一个和8相等的数就将ax的值+1
方法1
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov ax,.
mov cx,8
s:cmp byte ptr [bx],8
jne next
inc ax
next:
inc bx
loop s
mov ax,4c00h
int 21h
code ends
C++方法2
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov ax,0
mov cx,8
s:cmp byte ptr [bx],8
je ok
jmp short next
ok :inc ax
next:inc bx
loop s
mov ax,4c00h
int 21h
C++至于 大于8字节的个数 jna 和小于8的字节的个数 jnb 很相等也是大差不差
DF标志和串传送指令
在串处理指令中 控制每次操作后si di的增减
DF = 0 每次操作后si di递增
DF = 1 每次操作后si di递减
串传送指令1:movsb 功能以字节为单位传送
((es)*16+(di)) =((ds)*16+(si))
如果DF = 0 则 (si) = (si)+1
(di) = (di)+1
如果DF = 1则 (si) = (si)-1
(di) = (di)-1
串传送指令2
功能 以字为单位传送
((es)*16+(di)) = ((ds)*16+(si))
如果DF = 0 则 (si) = (si)+2
(di) = (di)+2
如果DF = 1则 (si) = (si)-2
(di) = (di)-2
对DF位进行设置的指令 cld指令将标志寄存器DF设为0
std指令 将标志寄存器的DF位设为1
data segment
db 'Welcome to masm!'
db 16 dup(0)
data ends
code segment
start:
mov ax,data
mov ds,ax
mov si,0
mov es,ax
mov di,16
cld
mov cx,16
s:movsb
loop s
mov ax,4c00h
int 21h
code ends
C++rep指令
rep指令常和串传送指令搭配使用
根据cx的值 重复执行后面的指令
rep movsb = s:movsb loop s
应用示例 用串传送指令 将F000H段中的最后16个字符复制到data段
段的最后一个字符的位置f000:ffff
assume cs:code,ds:data
data segment
db 16 dup(0)
data ends
code segment
start:
mov ax,0f000h
mov ds,ax
mov si,0ffffh
mov ax,data
mov es,ax
mov di,15
mov cx,16
std
rep movsb
mov ax,4c00h
int 21h
code ends
end start
C++