目录
(2)指令“call far ptr 标号”实现的时段间转移
6.1、“转移”综述
1、转移指令
可以控制CPU执行内存中某处代码的指令
可以修改IP、或同时修改CS和IP的指令
2、转移指令的分类
按转移行为 | 按指令对IP的修改范围 | 按转移指令 | ||||||
---|---|---|---|---|---|---|---|---|
段内转移 | 段间转移 | 段间短转移 | 段间近转移 | 无条件转移指令 | 条件转移指令 | 循环指令 | 过程 | 中断 |
只修改IP | 同时修改CS和IP | IP修改范围 | JMP | JCXZ | LOOP | |||
jmp ax | jmp 1000:0 | -128~127 | -32768~32768 |
6.2、操作符offset
1、作用
用操作符offset取得标号的偏移地址
格式为:offset 标号
例如:
assume cs:code
code segment
start:
mov ax,offset start
;相当于mov ax,0,标号所在地址为0
s:mov ax,offset s
;相当于mov ax,3,标号所在地址为3
mov ax,4c00h
int 21h
code ends
end start
2、练习
问题:有如下程序段,添加2条指令,使得该程序在运行中将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 s
;nop即空,机器码占一个字节,起到占位的作用
分析:
(1)s、s0处的指令所在的内存单元的地址分别是
cs:offset s和cs:offset s0
(2)将s处的数据复制到s0处,等价于
将cs:offset s处的数据复制到cs:offset s0处
(3)地址如何表示?
段地址在cs中,偏移地址在si、di中
(4)要复制的数据有多长?
mov ax,bx指令长度为两个字节,即一个字
补齐代码:
assume cs:code
code segment
s:mov ax,bx
mov si,offset s
mov di,offset s0
mov ax,cs:[si]
mov cs:[di],ax
s0:nop
nop
code ends
end s
6.3、jmp指令
1、无条件转移指令
(1)功能:无条件转移,可以修改IP,也可以同时修改CS和IP
(2)给出的两种信息:转移的目的地址、转移的距离
段间转移(远转移) | jmp 2000:1000 | |
---|---|---|
段内短转移 | jmp short 标号 | IP(-128~127)8位转移 |
段内近转移 | jmp near ptr 标号 | IP(-32768~32767)16位转移 |
2、依据位移进行转移
JMP short的机器指令中,包含的是跳转到指令的相对位置,而不是转移到目的地址。
assume cs:code
code segment
start:
mov ax,0
jmp short s
add ax,1
nop
nop
s:inc ax
code ends
end start
分析:jmp short s指令的读取和执行
(1)(IP)=0003,CS:IP指向EB 05(jmp的机器码)
(2)读取指令码EB 05进入指令缓冲器;
(3)(IP)=(IP)+所读取指令的长度 =(IP)+2 =0005,CS:IP指向add ax,0001;
(4)CPU执行指令缓冲器中的指令EB05;
(5)指令EB05执行后,(IP)=(IP)+05 = 000AH,CS:IP指向inc ax。
3、温馨提示
如果end start命令错写成ends start,debug挂载的时候则会出现:“access denied” 拒绝挂载的错误提示,编译连接的时候不会提示。
4、两种段内转移
(1)短转移:jmp short 标号
功能:IP=IP + 8位位移
8位位移 = 标号处的地址 - JMP指令后的第一个子节的地址
short指明此处的移位是8位位移
8位位移的范围为-128~127,用补码表示
8位位移由编译程序在编译时算出
(2)近转移:jmp near ptr 标号
功能:IP = IP + 16位位移
16位位移 = 标号处的地址 - JMP指令后的第一个子节的地址
near ptr指明此处的位移为16位位移,进行的是段内近转移
16位位移的范围为-32769~32767,用补码表示
16位位移由编译程序在编译时算出
5、远转移:jmp far ptr 标号
远转移jmp far ptr 标号 | 进转移jmp near ptr 标号 |
段间转移 | 段内转移 |
far ptr指明了跳转到的目的地址,包含了标号的段地址CS和转移地址IP | near ptr指明了相对于当前IP的转移,而不是转移的目的地址 |
assume cs:code code segment start:mov ax.0 mov bx,0 jmp far ptr s db 256 dup(0) s:add ax,1 inc ax code ends end start | assume cs:code code segment start:mov ax,0 mov bx,0 jmp near ptr s db 256 dup(0) s:add ax,1 inc ax code ends end start |
6、转移地址在寄存器中
指令格式:jmp 16位寄存器
功能:(IP)=(16位寄存器)
assume cs:code
code segment
start:
mov ax,0
mov bx,ax
jmp bx
mov ax,0123h
code ends
end start
7、转移地址在内存中
jmp word ptr 内存单元地址 | jmp dword ptr 内存单元地址 |
---|---|
段内转移 | 段间转移 |
从内存单元地址处开始存放着1个字,是转移的目的偏移地址 | 从内存单元地址处开始存放着2个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址 |
mov ax.0123h mov ds:[0],ax jmp word ptr ds:[0] 执行后, (IP)=0123h
mov ax,0123h mov [bx],ax jmp word ptr [bx] 执行后, (IP)=0123h | mov ax,0123h mov ds:[0],ax mov word ptr ds:[2],0 jmp dword ptr ds:[0] 执行后,(CS)=0; (IP)=0123h CS:IP指向0:0123h
mov ax,0123h mov [bx],ax mov word ptr [bx+2],0 jmp dword ptr [bx] 执行后,(CS)=0; (IP)=0123h CS:IP指向0:0123h |
8、JMP指令小结
6.4、其他转移指令
1、JCXZ指令
格式:jcxz 标号 (jmp cx ?= zero)
功能:若(cx)= 0,则转移到标号处执行;
若(cx)≠ 0,则什么也不做,程序向下执行。
(cx)= 0时,(IP)=(IP)+ 8
8位位移 = 标号 处的地址 - JCXZ指令后的第一个字节的地址
8位位移的范围是-128~123,用补码表示
8位位移由编译程序在编译时算出
注释:JCXZ是有条件转移指令
(1)所有的有条件转移指令都是短转移
(2)对IP的修改范围都为-128~127
(3)在对应的机器码中包含转移的位移,而不是目的地址
assume cs:codesg
codesg segment
start:
mov ax,2000h
mov ds,ax
moc bx,0
s:mov cx,[bx]
jcxz ok
inc bx
inc bx
jmp short s
ok:mov dx,bx
mov ax,4c00h
int 21h
codesg ends
end start
2、loop指令
3、根据位移进行相对转移的意义
对IP的修改是根据转移目的地址和转移起始地址之间的位移来进行的
jmp short 标号
jmp near ptr 标号
jcxz 标号
loop 标号
在它们对应的机器码中不包含转移的目的地址,而是包含的是到目的的地址的位移。
这样的设计,方便程序端在内存中的浮动装配。
6.5、call指令和ret指令
1、模块化程序设计
调用子程序:call
返回:ret
实质:流程转移指令,它们都修改IP,或者同时修改CS和IP
mov ax,0
call s
mov ax,4c00h
innt 21h
s:add ax,1
ret
2、call指令
(1)格式:call 标号
CPU执行call指令,进行两步操作
(1)将当前的IP或CS和IP压入栈中
(SP)=(SP)-2
((SS)*16 +(SP))=(IP)
(2)转移到标号处执行指令
(IP)=(IP)+16位位移
#相当于
push IP
jmp near ptr 标号
注释:
(1)16位位移 = “标号”处的地址 - call指令后的第一个字节的地址
(2)16位位移的范围是 -32768 ~ 32767,用补码表示
(3)16位位移由编译程序再编译时算出
(2)指令“call far ptr 标号”实现的时段间转移
call far ptr 标号相当于
push CS
push IP
jmp far ptr 标号
指令“call 标号”类似于“jmp near ptr 标号”,对应的机器指令中为对应于当前IP的转移位移,而不是转移的目的地址,实现段内转移;
指令“call far ptr 标号”实现段间转移。
(3)转移地址在寄存器中的call指令
指令格式:call 16位寄存器
相当于进行
push IP
jmp 16位寄存器
(4)转移地址在内存中的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) = 10h-2 = 0eh
call dword ptr 内存单元地址
相当于
push CS
push IP
jmp dword ptr 内存单元地址
示例:
mov 10h
mov ax,0123h
mov ds:[0],ax
;低地址放偏移地址
mov word ptr ds:[2],0
;高地址放段地址
call dword ptr ds:[0]
执行后,(CS)= 0,(IP)= 0123h,(SP)= 0ch
3、返回指令:ret和retf
ret指令 | retf指令 | |
---|---|---|
功能 | 用栈中的数据,修改IP的内容,从而实现近转移 | 用栈中的数据,修改CS和IP的内容,从而实现远转移 |
相当于 | pop IP | pop IP pop CS |
举例 | 示例1 | 示例2 |
;示例1
;p6-1.asm
assume cs:code,ss:stack
stack segment
db 16 dup(0)
stack ends
code segment
mov ax,4c00h
int 21h
start:
mov ax,stack
mov ss,ax
mov sp,16
mov ax,0
push ax
mov bx,0
ret
code ends
end start
;示例2
;p6-2
assume cs:code,ss:stack
stack segment
db 16 dup(0)
stack ends
code segment
mov ax,4c00h
int 21h
start:
mov ax,stack
mov ss,ax
mov sp,16
mov ax,0
push cs
push ax
mov bx,0
retf
code ends
end start
6.6、call和ret的配合使用
1、具有子程序的源程序框架
assume cs:code
code segment
main:...
call sub1
;调用子程序sub1
...
mov ax,4c00h
int 21h
sub1:...
call sub2
;调用子程序2
...
ret
sub2:...
...
ret
code ends
end main
2、应用
;示例
;计算2的N次方方,计算前,N的值由CX给出
;p6-3
assume cs:code,ss:stack
stack segment
db 16 dup(0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,16
mov ax,1
mov cx,3
call s
mov ax,4c00h
add 21h
s:add ax,ax
loop s
ret
code ends
end start
6.7、mul指令
1、格式
mul 寄存器
mul 内存单元
8位乘法 | 16位乘法 | |
---|---|---|
被除数 | AL | AX |
除数 | 8位寄存器或内存字节单元 | 16位寄存器或内存字单元 |
结果 | AX | DX(高位)、AX(低位) |
示例 | mul bl --(ax)=(al)*(bl)
mul byte ptr ds:[0] --(ax)=(al)*((ds)*16+0) | mul word ptr [bx+si+8] --(ax)=(ax)*((ds)*16+(bx)+(si)+8)结果的低16位 --(dx)=(ax)*((ds)*16+(bx)+(si)+8)结果的高16位 |
2、应用
示例1
;计算100*10
mov al,64
;100(O) = 64(H)
mov bl,a
;10(O) = a(H)
mul bl
示例2
;计算100*10000
mov ax,64
;100 = 64(H)
mov bx,2710
;10000 = 2710(H)
mul bx
6.8、汇编语言的模块化程序设计
1、参数和结果传递的问题
方案
用寄存器传递参数
用内存单元进行参数传递
用栈传递参数
(1)用寄存器来存储
用寄存器来存储参数和结果是最常用使用的方法
编译任务:计算data段中第一组数据的3次方,结果保存在后面一组dword单元中。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |||||||||
;p6-4.asm
;by c10udz
assume cs:code,ds:data,ss:stack
stack segment
db 16 dup(0)
stack ends
data segment
dw 1,2,3,4,5,6,7,8
dd 0,0,0,0,0,0,0,0
data ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
mov si,0
mov di,16
mov cx,8
s:mov bx,[si]
call cube
mov [di],ax
mov [di].2,dx
add si,2
add di,4
loop s
cube:mov ax,bx
mul bx
mul bx
ret
mov ax,4c00h
int 21h
code ends
end start
6.9、寄存器冲突问题
1、子程序标准框架
子程序开始:子程序中使用的寄存器入栈
子程序内容
子程序中使用的寄存器出栈
返回(ret、retf)
2、示例1
编程将给出的数据改为大写格式
1)源码
;p6-9.asm
assume cs:code,ds:data,ss:stack
data segment
db 'word',0
db 'unix',0
db 'wind',0
db 'good',0
data ends
stack segment
db 16 dup(0)
stack ends
code segment
start:mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,16
mov bx,0
mov cx,4
s:mov si,bx
call capital
add bx,5
loop s
mov ax,4c00h
int 21h
capital:push cx
push si
s0:mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111b
inc si
jmp short s0
ok:pop si
pop cx
ret
;子程序
code ends
end start
2)查看源码
3)结果比较
6.10、标志寄存器
1、标志(flag)寄存器的结构
PSW/FLAGS、别称:程序状态字
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
--- | --- | --- | --- | OF | DF | IF | TF | SF | ZF | --- | AF | --- | PF | --- | CF |
flag寄存器是按位起作用的,它的每一位都有专门的含义,记录特定的信息
2、标志寄存器的作用作用
1)用来存储相关指令的某些执行结果
2)用来为CPU执行相关指令提供行为依据
3)用来控制CPU的相关工作方式
3、标志寄存器值得解读
标志 | 值为1 | 值为0 | 意义 | 备注 | |
---|---|---|---|---|---|
OverFlow | OP | OV | NV | 溢出 | |
Direction | DF | DN | UP | 方向 | down/up |
Sign | SF | NG | PL | 符号 | negative/positive |
Zero | ZF | ZR | NZ | 零值 | |
Parity | PF | PE | PO | 奇偶 | odd/even |
Carry | CF | CY | NC | 进位 |
4、直接访问标志寄存器得方法
pushf:将标志寄存器的值压栈
popf:从栈中弹出数据,送入标志寄存器中。
5、各个标志详解
5.1、ZF-零标志(Zero Flag)
ZF标记相关指令的计算结果是否为0
1)ZF=1,表示“结果为0”,1表示逻辑真
2)ZF=0,表示“结果非0”,0表示逻辑假
5.2、PF-奇偶标志(Parity Flag)
PF记录指令执行后,结果的所有二进制位中1的个数
1)1的个数是偶数,PE=1
2)1的个数是奇数,PE=0
指令 | 执行结果 |
---|---|
mov al,1 add al,10 | 结果为0000 1011B = 0000 0001B + 0000 1010B 其中有3个1,则PF=0 |
mov al,1 or al,2 | 结果为0000 0011B = 0000 0001B or 0000 0010B 结果中有2个1,则PF=1 |
5.3、SF-符号标志(Sign Flag)
SF记录指令执行后,将结果视为有符号数
1)结果为负,SF=1
2)结果为非负,SF=0
另外:有符号数与补码
计算机中有符号数一律使用补码来表示和存储
1)正整数的补码使用二进制表示,与原码相同
2)负数的补码,将其对应的正数二进制的所有位取反,后加一
5.4、CF-进位标志(Carry Flag)
在进行无符号数运算时,CF记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。
CF记录指令执行后,
1)有进位或借位,CF=1
2)无进位或借位,CF=0
5.5、OF-溢出标志(Overflow Flag)
在进行有符号数运算的时候,如果结果超过了机器所能表示的范围称为溢出。
OF记录有符号数操作指令执行后,
1)有溢出,OF=1
2)无溢出,OF=0
另外,CF和OF的区别
1)CF是对无符号数运算有意义的进/借位标志位
2)OF是对有符号数运算有意义的溢出标志位
6、综合
一条指令会带来多个标志寄存器的变化
指令 | CF | OF | SF | ZF | PF |
---|---|---|---|---|---|
sub al,al | 0 | 0 | 0 | 1 | 1 |
mov al,10h | 0 | 0 | 0 | 1 | 1 |
add al,90h | 0 | 0 | 1 | 0 | 1 |
mov al,80h | 0 | 0 | 1 | 0 | 1 |
add al,80h | 1 | 1 | 0 | 1 | 1 |
mov al,0FCh | 1 | 1 | 0 | 1 | 1 |
add al,05h | 1 | 0 | 0 | 0 | 0 |
mov al,7Dh | 1 | 0 | 0 | 0 | 0 |
add al,0Bh | 0 | 1 | 1 | 0 | 1 |
6.11、带进位的加减法
1、adc指令
adc是带进位加法指令,它利用CF位上记录的进位值
1)格式:adc 操作对象1,操作对象2
2)功能:操作对象1 = 操作对象1 + 操作对象2+CF
3)例:adc ax,bx实现的功能是:(ax)=(ax)+(bx)+CF
指令 | mov al,98H add al,al adc al,3 | mov ax,1 add ax,ax adc ax,3 | mov ax,2 mov bx,1 sub bx,ax adc ax,1 |
---|---|---|---|
结果 | (al)=34H | (ax)=5 | (ax)=4 |
解释 | =(al)+3+CF =30H+3+1=34H | =(ax)+3+CF =2+3+0=5 | =(ax)+1+CF =2+1+1=4 |
2、应用(大数相加)
例如:编程计算1EF000H + 201000H,结果放在ax(高16位)和bx(低16位)中
mov ax,001E
mov bx,F000
add bx,1000
adc,ax,0020
结果:1E F000H + 20 1000H = 3F 0000H
3、sbb指令
sbb是带进位减法指令,它利用CF位上记录的借位值
1)格式:sbb 操作对象1,操作对象2
2)功能:操作对象1 = 操作对象1 - 操作对象2 - CF
3)例:sub ax,bx实现的功能是:(ax)=(ax)-(bx)-CF
应用:对任意大的数据进行减法运算
例如:计算003E 1000H - 0020 2000H,结果放在ax,bx中
mov ax,003e
mov bx,1000
sub ax,0020
sbb bx,2000
6.12、cmp和条件转移指令
6.13、条件转移指令应用
6.14、DF标志和串传送指令