转移概念
一般情况下指令是顺序逐条执行,然而在实际情况中常常需要改变程序的执行流程。
转移指令:
- 可以控制CPU执行内存中某处代码的指令
- 可以修改IP,或者同时修改CS和IP的指令
转移指令按行为分为:
- 段内转移:只修改IP,如jmp ax
- 段间转移:同时修改CS和IP,如jmp 1000:0
根据指令对IP修改的范围不同分为:
- 段内短转移: IP修改范围为-128~127
- 段内近转移: IP修改范围为-32768~32767
按转移指令分为:
- 无条件转移指令:jmp
- 条件转移指令:jcxz
- 循环指令:loop
- 过程
- 中断
offset偏移地址
格式
offset 标号
assume cs:codeseg
codeseg segment
start: mov ax, offset start ; 相当于mov ax,0s:mov ax, offset s ; 相当于mov ax,3
codeseg ends
end start
jmp指令,无条件转移
不仅可以修改IP,也可以同时修改CS和IP。
短转移
“jmp short 标号” 的机器指令中,包含的是跳转到指令的相对位置,而不是转移的目标地址,(IP) = (IP) + 8位位移
- 8位位移 = '标号’处的地址减去jmp指令后的第一个字节的地址;
- short指明此处的位移为8位位移;
- 8位位移的范围为-128~127,用补码表示;
- 8位位移由编译程序在编译时算出
近转移
指令: “jmp near ptr 标号”,功能:(IP) = (IP) + 16位位移
- 16位位移="标号"处的地址减去jmp指令后的第一个字节的地址;
- near ptr指明此处的位移为16位位移,进行的段内近转移;
- 16位位移的范围为-32768~32767,采用补码表示;
- 16位位移由编译程序在编译时算出;
远转移
指令: “jmp far ptr 标号”
远转移jmp far ptr 标号 | 近转移jmp near ptr 标号 |
---|---|
段间转移 | 段内转移 |
far ptr指明跳转到的目的地址,即包含了标号的段地址CS和偏移地址IP | near ptr指明了相对于当前IP的转移位移,而不是转移的目的地址 |
assume cs:codesg
codesg segment
start: mov ax,0mov bx,0jmp far ptr sdb 256 dup(0)s:add ax,1inc ax
codesg ends
end start
转移地址在寄存器中的jmp指令
指令格式:jmp 16位寄存器
assume cs:codesg
codesg segment
start: mov ax,0mov bx,axjmp bxmov ax,0123H
codesg ends
end start
转移地址在内存中的jmp指令
jmp word ptr内存单元地址 | jmp dword ptr内存单元地址 |
---|---|
段内转移 | 段间转移 |
功能:从内存单元地址处开始存放着一个字,是转移的目的偏移地址。 | 功能:从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址。 00 — (IP) 02 — (CS) |
段内偏移
mov ax,0123H
mov ds:[0],ax
jmp word ptr ds:[0] ; (IP)=0123H
段间转移
mov ax,0123H
mov [bx],ax
mov word ptr [bx+2],0
jmp dword ptr [bx]
;执行后
; (CS) = 0
; (IP) = 0123H
; CS:IP指向0000:0123
jcxz指令
指令格式:jcxz 标号 (cx为cx寄存器,z为zero)
功能: 如果cx等于0,则转移到标号处执行,当cx不等于0时,则什么也不做,向下执行。
- 当cx = 0时,(IP) = (IP) + 8位位移
- 8位位移="标号"处的地址减去jcxz指令后的第一个字节的地址;
- 8位位移的范围为-128~127,用补码表示;
- 8位位移由编译程序在编译时算出;
- jcxz是有条件转移指令
- 所有的有条件转移指令都是短转移;
- 对IP的修改范围都为-128~127;
- 在对应的机器码中包含转移的位移,而不是目的地址;
assume cs:codesg
codesg segment
start:mov ax,2000Hmov ds,axmov bx,0s:mov cx,[bx]jcxz okinc bxinc bxjmp short sok:mov dx,bxmov ax,4c00Hint 21Hcodesg endsend start
loop指令
指令格式: loop标号
指令操作:
-
(cx) = (cx) - 1;
-
当(cx) != 0时,转移到标号处执行;
当(cx) = 0时,程序向下执行;
assume cs:codesg
codesg segment
start:mov cx,6hmov ax,10hs:add ax,axloop smov ax,4c00hint 21h
codesg ends
end start
模块化设计
call指令
调用子程序,call指令实现转移的方式与jmp指令的原理相似。
格式:call 标号
CPU执行call指令:
- 将当前IP或CS和IP压入栈中
- 转移到标号处执行指令
call 标号:
- 16位位移="标号"处的地址减去call指令后的第一个字节的地址;
- 16位位移的范围为-32768~32767,用补码表示;
- 16位位移由编译程序在编译时算出;
mov ax,0
call s
mov ax,4c00h
int 21hs: add ax,1ret
指令"call for ptr 标号"-不同段间转移
-
(sp) = (sp) - 2
((ss) * 16 + (sp)) = (cs)
(sp) = (sp) - 2
((ss) * 16 + (sp)) = (ip)
-
(cs) = 标号所在的段地址
(IP) = 标号所在的偏移地址
该指令相当于
push cs
push ip
jmp far ptr 标号
mov ax,0
call far ptr s
; ...
mov ax,4c00h
int 21hs:add ax,1ret
转移地址在内存中的call指令
call word ptr 内存单元地址
相当于:
- push IP
- jmp word ptr 内存单元地址
mov sp,10h
mov ax,0123h
mov ds:[0],ax
call word ptr ds:[0]
; 执行之后,(IP)=0123H,(SP)=OEH
call dword ptr 内存单元地址
相当于:
- push CS
- push IP
- jmp dword ptr 内存单元地址
mov sp,10h
mov ax,0123h
mov ds:[0],ax ; 低地址放置偏移地址
mov word ptr ds:[2],0 ; 高地址放置段地址
call word ptr ds:[0]
; 执行结果为:(CS) = 0,(IP) = 0123H,(sp) = 0CH
返回指令ret和retf
ret指令采用栈中的数据,修改IP的内容,从而实现近转移。相当于pop IP。
assume cs:codesg,ss:stack
stack segmentdb 16 dup(0)
stack ends
codesg segmentmov ax,4c00hint 21h
start: mov ax,stackmov ss,axmov sp,16mov,ax,0push ax ; 返回的ax值(IP)mov bx,0ret
codesg ends
end start
retf采用栈中的数据,修改CS和IP的内容,从而实现远转移。相当于:
- pop IP
- pop CS
assume cs:codesg,ss:stack
stack segmentdb 16 dup(0)
stack ends
codesg segmentmov ax,4c00hint 21h
start: mov ax,stackmov ss,axmov sp,16mov,ax,0push cs ; 返回的cs值(CS)push ax ; 返回的ax值(IP)mov bx,0ret
codesg ends
end start
call 和 ret的配合使用
assume cs:code,ss:stack
stack segmentdb 8 dup(0)db 8 dup(0)
stack ends
code segment
start : mov ax,stackmov ss,axmov sp,16mov ax,1000call smov ax,4c00hint 21hs: add ax,axret
code ends
end start
mul乘法指令
- mul寄存器
- mul内存单元
8位乘法 | 16位乘法 | |
---|---|---|
被乘数 | AL | AX |
乘数 | 8位寄存器或内存字节单位 | 16位寄存器或内存字单元 |
结果 | AX | DX(高位)和AX(低位) |
mul bl mul byte ptr ds:[0] | mul word ptr [bx+si+8] ;ax存放结果的低16位 dx存放结果的高16位 |
示例
; 100 * 10
mov al,100
mov bl,10
mul bl
; 结果: (ax) = 1000 (03E8H)
; 100 * 10000
mov ax,100
mov bx,10000
mul bx
; 结果(bx) = 000FH (ax) = 4240H
; F4240H = 1000000
模块化程序设计
采用寄存器存储参数和结果
- 参数放置在bx中,即(bx) = N
- 子程序中采用多个指令进行运算
- 将结果放置在dx和ax中
; 计算data段中第一组数据的3次方,结果保存在后一组dword单元中
assume cs:code, ds:data
data segmentdw 1,2,3,4,5,6,7,8dd 0,0,0,0,0,0,0,0
data endscode segment
start: mov ax,datamov ds,axmov si,0mov di,16mov cx,8s:mov bx,[si]call cubemov [di], axmov [di].2, dx ; 作用同上一行一致add si,2add di,4loop smov ax,4c00hint 21hcube: mov ax,bxmul bxmul bxret
code ends
end start
采用内存单元批量传递数据
-
将批量数据存放在内存中,随后将它们所在内存空间的首地址放置在寄存器中,传递给需要的子程序。
-
对于具有批量数据的返回结果,可用同样的方式。
示例如下:
; 将data段中的字符串转换为大写
assume cs:codedata segmentdb 'conversation'
data endscode segment
start: mov ax,datamov ds,axmov si,0mov cx,12call capitalmov ax,4c00hint 21h
capital:and byte ptr [si], 11011111binc siloop capitalret
code ends
end start
采用栈传递参数
将需要传递给子程序的参数压入栈中,子程序从栈中取得参数。
需要特别注意指令"ret n"的作用
其它数据 |
---|
返回点IP |
a参数 |
b参数 |
pop ip
add sp,n ; 舍弃n个参数直接偏移到栈首
示例描述: 计算(a-b)^3,a,b为word型数据。
- 进入子程序前,参数a,b入栈
- 调用子程序,使栈顶存放IP
- 结果: (dx:ax) = (a - b) ^ 3
assume cs:code
code segment
start: mov ax,1push axmov ax,3push axcall difcubemov ax,4c00hint 21h
difcube:push bp ; 将bp之前的数值保留mov bp,spmov ax,[bp + 4] ; 获取栈中第一个参数sub ax,[bp + 6] ; 获取栈中第二个参数mov bp,axmul bpmul bppop bp ; 恢复进入段之前的数据ret 4 ; 由于压入的两个参数,跳过两个参数直接偏移到栈首
code ends
子程序编写标准框架
-
子程序开始: 子程序中使用的寄存器入栈
子程序内容
子程序使用的寄存器出栈
返回(ret,retf)
在子模块开始时,将要用到的寄存器内容全都保存,在子程序返回前在恢复。
assume cs:code
data segmentdb 'word',0db 'unix',0db 'wind',0db 'good',0
data endscode segment
start: mov ax,datamov bx,0mov cx,4s:mov si,bxcall capitaladd bx,5loop smov ax,4c00hint 21hcapital:push cxpush sichange: mov cl,[si]mov ch,0jcxz okand byte ptr [si], 11011111binc sijmp short changeok: pop sipop cxret
code ends
end start
标志寄存器
flag寄存器是按位标记,每一个都有不同的含义,记录特定的信息。
- 存储相关指令的某些执行结果
- 为CPU执行相关指令提供行为依据
- 控制CPU的相关工作方式
标志 | 值为1 | 值为0 | 意义 | |
---|---|---|---|---|
Overflow | OF | OV | NV | 溢出 |
Direction | DF | DN | UP | 方向 |
Sign | SF | NG | PL | 符号 |
Zero | ZF | ZR | NZ | 零值 |
Parity | PF | PE | PO | 奇偶 |
Carry | CF | CY | NC | 进位 |
ZF-零标志(Zero Flag)
标记相关指令的计算结果是否为0
- ZF = 1,表示"结果为0",1表示逻辑真
- ZF = 0,表示"结果不为0",0表示"逻辑假"
示例
指令 | 执行结果 |
---|---|
mov ax,1 and ax,0 | ZF =1,表示"结果为0" |
mov ax,1 or ax,0 | ZF = 0,表示"结果非0" |
PF-奇偶标志(Parity Flag)
记录指令执行之后,结果当中所有二进制位中1的个数
- 1的个数为偶数,PF = 1
- 1的个数为奇数,PF = 0
SF-符号标志(Sign Flag)
SF记录指令执行后,将结果视为有符号数
- 结果为负数,SF = 1
- 结果为非负,SF = 0
示例
指令 | 执行结果 |
---|---|
mov al,10000001 add al,1 | 结果al为10000010 为负数,则SF = 1 |
sub ax,ax | 结果ax为0,为非负数,SF = 0 |
SF标志是CPU对有符号数运算结果的一种记录,将数据当作有符号数运算时,通过SF可知结果的正负,将数据当作无符号数运算时,SF没有意义。
CF-进位标志(Carry Flag)
进行无符号数运算时,CF记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位。
CF指令执行之后
- 有进位或借位,CF = 1
- 无进位或借位,CF = 0
示例
指令 | 执行结果 |
---|---|
mov al,98h add al al | (al) = 30h,CF=1,CF记录了最高有效位向更高位的进位值 |
add al,al | (al) = 60h,CF=0,CF记录了最高有效位向更高位的进位值 |
sub al,98h | (al) = C8h,CF=1,CF记录了最高有效位向更高位的借位值 |
OF-溢出标志(Overflow Flag)
进行有符号数运算时,如结果超过了机器所能表示的范围称为溢出
OF记录有符号数操作指令执行之后的状态
- 有溢出,OF = 1
- 无溢出,OF = 0
指令 | 执行结果 |
---|---|
mov al,98 add al,99 | (al) = 197,超出了8位有符号数的范围(-128~127),OF = 1 |
mov al,0F0H add al,88H | (al) = (-16) + (-120) = -136,有溢出,OF = 1 |
CF和OF的区别
- CF是对无符号数运算有意义的进/借位标志位
- OF是对有符号数运算有意义的溢出标志位
指令 | 执行结果 |
---|---|
mov al,0F0H add al,88H | CF = 1, OF = 1,当无符号数运算有进位,当有符号数运算有溢出。 |
adc带进位加法指令
adc带进位加法指令,利用CF位上记录的进位值
- 格式: adc 操作对象1,操作对象2
- 功能: 操作对象1 = 操作对象1 + 操作对象2 + CF
指令 | mov al,98h add al,al adc al,3 | mov ax,1 add ax,ax adc ax,3 |
---|---|---|
结果 | (ax) = 34H | (ax) = 5 |
adc执行时,相当于计算 (ax)+3+CF=30H+3+1 = 34H | adc执行时,相当于计算 (ax)+3+CF=2+3+0=5 |
sbb带借位减法指令
- 格式:sbb 操作对象1, 操作对象2
- 功能: 操作对象1 = 操作对象1 - 操作对象2 - CF
- 利用CF位上记录的借位值
- (ax) = (ax) - (bx) - CF
; 计算003E1000H - 00202000H结果放在ax,bx中
; 对任意大的数据进行减法运算
mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020H
cmp指令
cmp是比较指令,功能相当于减法指令,不保存结果,但指令执行后,将对标志寄存器产生影响。
- 格式: cmp 操作对象1, 操作对象2
- 功能: 计算操作对象1减去操作对象2
指令 | cmp ax,ax | mov ax,8 mov bx,3 com ax,bx |
---|---|---|
功能 | 做(ax) - (ax)的运算,结果为0,但并不在ax中保存,仅影响flag的标志位 | (ax) = 8, (bx) = 3 |
标志寄存器 | ZF = 1,PF = 1,SF = 0,CF = 0,OF = 0 | ZF = 0, PF = 1,SF = 0, CF = 0, OF = 0 |
无符号数比较与标志位取值
通过cmp指令执行后查看相关标志位的值,
比较关系 | (ax) ? (bx) | (ax) - (bx)特点 | 标志寄存器 |
---|---|---|---|
等于 | (ax) = (bx) | (ax) - (bx) = 0 | ZF = 1 |
不等于 | (ax) != (bx) | (ax) - (bx) != 0 | ZF = 0 |
小于 | (ax) < (bx) | (ax) - (bx) 将产生借位 | CF = 1 |
大于等于 | (ax) >= (bx) | (ax) -(bx) 不必借位 | CF = 0 |
大于 | (ax) > (bx) | (ax) - (bx)既不借位,结果也不为0 | CF = 0 且 ZF = 0 |
小于等于 | (ax) <= (bx) | (ax) - (bx)或借位,或结果为0 | CF = 1 或 ZF = 1 |
有符号数比较与标志位取值
通过cmp进行有符号数比较(cmp ah bh)。
结果需要(SF)配合是否溢出(OF)得出结论。
比较关系 | (ax) ? (bx) | (ax) - (bx)特点 | 标志寄存器 |
---|---|---|---|
等于 | (ah) = (bh) | (ah) - (bh) = 0 | ZF = 1 |
不等于 | (ah) != (bh) | (ah) - (bh) != 0 | ZF = 0 |
小于 | (ax) < (bx) | (ax) - (bx)为负,且不溢出 | SF = 1且OF = 0 |
大于 | (ax) > (bx) | (ax) - (bx)为负,且溢出 | SF = 1且OF = 1 |
大于等于 | (ax) >= (bx) | (ax) - (bx)为非负,且无溢出 | SF = 0且OF = 0 |
小于等于 | (ax) <= (bx) | (ax) - (bx)为非负,且有溢出 | SF = 0或OF = 1 |
条件转移指令
根据单个标志位转移的指令
指令 | 含义 | 测试条件 |
---|---|---|
je/jz | 相等/结果为0 | ZF = 1 |
jne/jnz | 不等/结果不为0 | ZF = 0 |
js | 结果为负数 | SF = 1 |
jns | 结果非负 | SF = 0 |
jo | 结果溢出 | OF = 1 |
jno | 结果不溢出 | OF = 0 |
jp | 奇偶位为1 | PF = 1 |
jnp | 奇偶位不为1 | PF = 0 |
jb/jnae/jc | 低于/不高于等于/有借位 | CF = 1 |
jnb/jae/jnc | 不低于/高于等于/无借位 | CF = 0 |
根据无符号数比较结果进行转移的指令
指令 | 含义 | 测试条件 |
---|---|---|
jb/jnae/jc | 低于则转移 | CF = 1 |
jnb/jae/jnc | 低于则转移 | CF = 0 |
jna/jbe | 不高于则转移 | CF = 1或ZF = 1 |
ja/jnbe | 高于则转移 | CF = 0且ZF = 0 |
根据有符号数比较结果进行转移的指令
指令 | 含义 | 测试条件 |
---|---|---|
jl/jnge | 小于则转移 | SF = 1且OF = 0 |
jnl/jge | 不小于转移 | SF = 0且OF = 0 |
jle/jng | 小于等于则转移 | SF = 0或OF = 1 |
jnle/jg | 不小于等于则转移 | SF = 1且OF = 1 |
j = Jump e = Equal n = Not b = Below a = Above l = Less g = Greater s = Sign c = Carry
p = Parity o = Overflow z = Zero
条件转移指令的使用
- 条件转移指令和cmp指令配合使用,构造条件转移指令
- 不必考虑cmp指令对相关标志位的影响和条件转移指令对相关标志位的检测
示例
; 如果ah = bh,则ah = ah + ah,否则ah = ah + bhcmp ah,bhje sadd ah,bhjmp short oks:add ah,ah
ok:ret
应用示例
统计数值为8的字节个数。
code segment
start:mov ax,datamov ds,axmov bx,0mov ax,0mov cx,8s:cmp byte ptr [bx],8je okjmp short nextok:inc axnext:inc bxloop smov ax,4c00hint 21h
code ends
统计数值大于8的字节个数
; 找到大于8的数就将ax的值加1
code segment
start:mov ax,datamov ds,axmov bx,0mov ax,0mov cx,8s:cmp byte ptr [bx],8jna nextinc axnext:inc bxloop smov ax,4c00hint 21h
code ends
end start
统计数值小于8的字节个数
; 找到小于8的数将ax的值加1
code segment
start:mov ax,datamov ds,axmov bx,0mov ax,0mov cx,8s:cmp byte ptr [bx],8jnb nextinc axnext:inc bxloop smov ax,4c00hint 21h
code ends
end start
DF标志和串传送指令
DF方向标志位
- 在串处理指令当中,控制每次操作后si,di的增减。
- DF = 0,每次操作后si,di递增
- DF = 1,每次操作后si,di递减
对DF位进行设置的指令
- cld指令: 将标志寄存器的DF位设置为0(clear)
- std指令: 将标志寄存器的DF位设置为1(setup)
串传送指令movsb
-
((es) * 16 + (di)) = ((ds) * 16 + (si))
-
如果DF = 0,则
- (si) = (si) + 1
- (di) = (di) + 1
如果DF = 1,则
- (si) = (si) - 1
- (di) = (di) - 1
串传送指令movsw
-
((es) * 16 + (di)) = ((ds) * 16 + (si))
-
如果DF = 0,则
- (si) = (si) + 2
- (di) = (di) + 2
如果DF = 1,则
- (si) = (si) - 2
- (di) = (di) - 2
示例
assume cs:codesg,ds:data
data segmentdb 'Welcome to masm!'db 16 dup(0)
data endscodesg segmentstart:mov ax,datamov si,0mov es,axmov di,16cldmov cx,16s:movsbloop smov ax,4c00hint 21h
codesg ends
end start
rep指令
该指令常常与串传送指令搭配使用。
-
根据cx的值,重复执行后面的指令
rep movsb 相当于
s: movsb
loop s
-
rep movsw 相当于
s: movsw
loop s
示例
assume cs:code,ds,data
data segmentdb 'Welcome to masm!'db 16 dup(0)
data endscode segment
start:mov ax,datamov ds,axmov si,0mov es,axmov di,16cldmov cx,8rep movswmov ax,4c00hint 21h
code ends
end start
将F000H段中的最后16个字符复制到data段中(F000FFFFH)。
assume cs:code,ds:data
data segmentdb 16 dup(0)
data ends
code segment
start:mov ax,F000Hmov ds,axmov si,FFFFHmov ax,datamov es,axmov di,15mov cx,16stdrep movsbmov ax,4c00Hint 21h
code ends
start end
注意事项
- 在编写调用子程序的程序时,注意看下子程序中有没有用到会产生冲突的寄存器,如果有,则使用其它寄存器。
- 在编写子程序时,不要使用会产生冲突的寄存器。
示例
将data段中的字符串转换为大写。(通过将数据段尾加上0来做判断语句结束)
assume cs:code ds:data
data segmentdb 'conversation',0
data endscode segment
start: mov ax,datamov ds,axmov si,0call capitalmov ax,4c00hint 21h
capital:mov cl,[si]mov ch,0jcxz okand byte ptr [si], 11011111binc sijmp short capitalok:ret
code ends
end start; 以上子程序依次读取每个字符进行检测,如果不是0,进行大写的转换,如果是0,结束处理。不再需要字符串的长度作为参数。
在运行中将s处的一条指令复制到s0处。
assume cs:codesg
codesg segment
s: mov ax,bxmov si,offset smov di,offset s0mov ax,cs:[si]mov cs:[di],ax
s0: nopnop
codesg ends
ends
; nop机器码占一个字节,起占位作用