流程转移与子程序
流程转移与子程序
目录
预备知识
立即数在机器指令中,会在内存里有体现
转移综述
- 转移指令分类
- 转移行为
- 段内转移:只修改IP,如:jmp ax
- 段间转移:同时修改CS、IP,如:jmp 1000:0
- IP 修改范围
- 段内短转移:IP修改范围为 -128 ~ 127
- 段内近转移:IP修改范围为 -32768 ~ 32767
- 转移指令
- 无条件转移指令(jmp)
- 条件转移指令(jcxz)
- 循环指令(loop)
- 过程
- 中断
- 转移行为
jmp指令 无条件转移
注意事项: 在源程序中,不允许使用
jmp 2000:2
形式的指令
- 可在debug中使用
- 汇编编译器不认识、会报错
原理:对执行 IP地址 的偏移 进行转移
原理示例:
跳转到标号s 标号s对应指令地址 - 076A:000A
jmp指令地址 - 076A:0003 指令内容 - EB05
- EB 为jmp指令
- 05为偏移地址 = s指令地址(000A) - jmp下一条指令地址(0003+2)
jmp指令格式 | 示例 |
---|---|
jmp标号 | - 段间转移(远转移): jmp far ptr标号 - 段内短转移: jmp short标号; 8位的位移 - 段内近转移: jmp near ptr标号; 16位的位移 |
jmp寄存器 | jmp bx; 16位的位移 |
jmp内存单元(表示跳转到的地址) | - 段内转移: jmp word ptr内存单元地址; jmp word ptr[bx] - 段间转移: jmp dword ptr内存单元地址; jmp dword ptr[bx] |
标号转移
段内转移
实质:相对当前IP的转移位移 功能:(IP) = (IP) + 位移
短转移:jmp short 标号
近转移:jmp near ptr 标号
- 位移=’标号’地址 - jmp后一个指令的地址
- (short 8位,near ptr 16位)
- 用补码表示(8位范围:-128-127 16位范围:-32768-32767)
- 若是超出范围,会报错
远转移
原理:覆盖CS:IP
jmp far ptr 标号
jmpi 于 jmp far 等价,同为远转移指令,由于历史遗留问题,存在两种写法
1
2
3
4
5
; jmp far 写法
jmp far 0x1000:0x1234
; jmpi 写法
jmpi 0x1000, 0x1234
寄存器转移
实质:IP = (16位寄存器) jmp ax
内存转移
- jmp word ptr 内存单元地址
- jmp dword ptr 内存单元地址
offset 操作符
使用方法:offset 标号
作用:取得 标号对应指令的偏移地址
同时,offset 可用于 数据的标号,用于赋值方便
1
2
3
4
5
6
assume cs:code
code segment
a:db 1,2,3,4,5,6
b:dw 0
start: mov si, offset a
mov bx, offset b
其它条件转移/短转移指令
条件转移
- 所有的条件转移,均为短转移指令
- IP位移范围均为 -128-127
- 机器码中包含的是位移、而非目的地址
jcxz
格式:jcxz 标号
功能:根据 (cx) 是否等于 0,来决定是否转移
- 转移到标号 cx = 0
- 向下执行 $cs\neq0$
具体位移操作:
(IP) = (IP) + 8位位移地址
- 8位位移 = 标号地址 - jcxz后一个指令地址
- 范围 -128-127, 用补码表示
loop
格式:loop 标号
操作:
- (cx)=(cx)-1
- (cx)$\neq0$,转移到标号
具体位移操作: 与段转移相同,用负数的补码表示位移
XCHG
(exchange)数据交换指令
格式:xchg opr1, opr2
作用: 交换两个内存单元中的内容
注意:
* 不允许使用段寄存器 * 不影响标志位 * 不能直接交换两个内存地址的值 * 需要有一个是寄存器
XLAT
(Translate Byte)换码指令
格式:xlat
, xlat opr
功能:计算DS:BX + AL [+ opr]
指向的地址的值取出,放入 AL
中
操作:$(AL)\leftarrow ((BX)+(AL))$
注:
- opr可以是数据标号(偏移地址)
- 不影响标志位
- 字节表格(查找长度不超过256)
bx
作为首地址al
为位移量
lea
(Load Effective Address) 加载有效地址 格式:lea reg, src
功能:将有效地址加载到寄存器中,加载地址一般是标号或偏移地址
lds
(Load Pointer into DS)加载指针到DS
格式:lds reg, src
功能:从内存位置加载一个双字(32位),分别作为偏移地址和段地址,前2个字节放入寄存器,后2个字节放入DS段寄存器
les
(Load Pointer into ES)加载指针到ES
格式:les reg, src
功能:从内存位置加载一个双字(32位),分别作为偏移地址和段地址,前2个字节放入寄存器,后2个字节放入ES段寄存器
标志寄存器传送指令
LAHF
(Load AH from Flags)加载标志位到AH
格式:lahf
功能:把标志寄存器的低 8 位(状态标志) 加载到 AH
寄存器
SAHF
(Save AH from Flags)加载标志位到AH
格式:sahf
功能:把AH
寄存器的值 加载到 标志寄存器的低 8 位(状态标志)
PUSHF
(Push Flags)标志寄存器压栈
格式:pushf
操作:
- $(SP)\leftarrow (SP)-2$
- $((SP)+1, (SP))\leftarrow (FLAGS)$
POPF
(Pop Flags)出栈一个字到标志寄存器
格式:popf
操作:
- $(FLAGS)\leftarrow ((SP)+1, (SP))$
- $(SP)\leftarrow (SP)+2$
扩展指令
CBW
(Convert Byte to Word)字节转字
格式:cbw
功能:扩展 (AL)到 (AX)上,对AL进行符号扩展
- AL最高有效位为 0时,(AH)=00H
- AL最高有效位为 1时,(AH)=FFH
CWD
(Convert Word to Doubleword)字转双字 格式:cwd
功能:扩展 (AX)到 (DX, AX)上,对AX进行符号扩展
- AX最高有效位为 0时,(DX)=0000H
- AX最高有效位为 1时,(AH)=FFFFH
十进制调整指令
BCD码表示
用二进制编码的十进制数,又称二–十进制数
压缩的BCD码:用 4 位二进制数表示 1 位十进制数
例:$( 59 )10 =( 0101\ 1001 ){BCD}$
非压缩的BCD码:用 8 位二进制数表示 1 位十进制数
例:$( 59 )10 =( 0000\ 0101\ \ 0000\ 1001 ){BCD}$
ASCII码属于非压缩的BCD码
调整指令作用:在进行2进制的算数运算后,调整为十进制的表示
(1)压缩的BCD码调整指令
● DAA 加法的十进制调整指令
● DAS 减法的十进制调整指令
(2)非压缩的BCD码调整指令
● AAA 加法的ASCII码调整指令
● AAS 减法的ASCII码调整指令
● AAM 乘法的ASCII码调整指令
● AAD 除法的ASCII码调整指令
使用示范
在算术运算后,使用对应指令进行调整
1
2
3
4
5
6
7
8
9
10
11
(1)
MOV AL, BCD1 ; BCD1=34H
ADD AL, BCD2 ; BCD2=59H, (AL)=8DH
DAA ; 8DH+06H=93H
MOV BCD3, AL ; BCD3=93H
(2)
MOV AL, BCD1 ; BCD1=34H
SUB AL, BCD2 ; BCD2=59H , (AL)=0DBH
DAS ; 0DBH-60H-06H=75H
MOV BCD3, AL ; BCD3= 75 = - 25
call 和 ret指令
使用必须预先设置栈空间
调用:call
返回:ret
call
实质:
- 将 call 后指令IP(当前IP — 执行指令时,IP会先调到下一条指令) 或 CS和IP 压入栈
- (sp)=(sp) - 2 (若是压入CS和IP,则先压入CS后IP)
- ((sp) $\times16$ +(sp)) = (IP)
- IP = IP + 位移(标号处 IP - 当前IP) / 覆写CS、IP
位移范围:16位位移 —— -32768 ~ 32767 用补码表示
该位移由编译程序在编译时自动算出
标号转移
段内转移
格式:call 标号
步骤: IP压栈 - 转移
段间转移 格式:call far ptr 标号
步骤:CS、IP压栈 - 转移
寄存器
格式:call 16位寄存器
实质:将寄存器中的值,作为跳转IP
步骤:IP压栈 - jmp 16位寄存器
内存
格式:call word/dword ptr ds:[0]内存单元地址
步骤:IP 或 CS/IP 压栈 - 跳转到 jmp word/dword ptr 内存单元
ret 和 retf
实质:从栈内取出 IP 或 CS/IP, 覆写转移 格式:ret
、 retf
步骤:pop IP(ret) 或 CS/IP(retf)
格式:ret n
步骤:
- pop ip
- add sp, n
参数传递方法-模块化程序设计
- 寄存器传递
- 内存单元传递
- 栈传递
方向标志位 DF、串传送指令、重复指令rep
作用:用于复制长串数据
DF
DF(Direction Flag)
作用:标志复制的移动方向
- 0 – 往前移
- 1 – 往后移
相关指令:
- cld (clear direction) – DF清零
- std (set direction) – DF置1
串传送指令
movsb / movsw
作用:移动 ds:si 中的数据 到 es:di 中 步骤:
- es:di 内存单元 = ds:si
- di、si的值增、或减
- df=0 增
- df=1 减
- movsb – 移动一个字节 si = si $\pm$ 1
- movsw – 移动一个字长 si = si $\pm$ 2
- stosb / stosw – 存储/初始化内存指令
- 将
AL
/AX
中的数据存储到 目标指定位置(di 指向位置) - 更新 di
- 将
- lodsb / lodsw – 从内存加载数据到寄存器
- 将源地址(si 指向位置)处的字节数据 加载到
al
/ax中
- 更新 si
- 将源地址(si 指向位置)处的字节数据 加载到
- cmpsb / cmpsw – 比较
si
和di
指向的字/字节- 相等, 标志寄存器中,ZF位 置1
- 不等,ZF 置0
- scasb / scasw – 比较
al
/ax
与di
指向的数据- 相等,ZF=1
- 不等,ZF=0
- 可与
repe
/repne
结合使用
串重复前缀
- 与 cx 组合使用,每执行 1次,cx -1
- 条件判断: 在cx的前提下,判断条件
REP
REPE
/REPZ
– 是否相等,ZF=1 执行REPNE
/REPNZ
– 是否不相等,ZF=0 执行
rep
作用:重复一个指令 cx 中值的次数,通常配合串传送指令使用
等价于:
1
2
s: 指令
loop s
repe / repz
repne / repnz
寄存器冲突问题和 程序化处理字符串
字符串处理程序化
原理:设置哨兵 0
步骤:
- 字符串后加上 0
- 进行字符串操作前,复制字符到 cl, 并给 ch 置0
- 使用 jcxz 跳出循环
- 字符串操作 用 jmp 死循环
寄存器冲突问题
解决方案:
将子程序中用到的寄存器 压入栈中,退出子程序时,弹出
标志寄存器/程序状态字
标志(flag)寄存器/程序中状态字: PSW/FLAGS
结构
8086CPU中:
- 有十六位的存储结构
- 没有使用flag的 1、3、5、12-15位
作用
寄存器的值和直接访问
PL 是 Plus的缩写,表示正数 或 零
直接访问的方法:
- pushf: 将标志寄存器的值压栈
- popf: 从栈中弹出数据,送入标志寄存器中 绝大多数时候,影响标志寄存器值的是 通过 算数或逻辑运算指令 进行控制
ZF_零标志
- 结果:0;ZF=1, 标志ZR
- 结果:非零;ZF=0, 标志NZ
PF_奇偶标志
含义:表示结果中 1 的个数的奇偶
- 结果:1为偶数;PF=1, 标志PE
- 结果:1为奇数;PF=0, 标志PO
SF_符号标志
含义:
- 作为有符号数计算结果的记录
将数据看作无符号数时,则SF的值无意义
- 结果:负;SF=1,NG
- 结果:正;SF=0,PL
计算机中,有符号数一律使用补码表示
原码变补码:
- 正数:与原码相同
- 负数:将其对应的正数原码,全部取反(包括符号位) + 1
补码变原码:
- 正数:不变
- 负数:取反+1
CF_进位标志
含义:
- 记录 无符号数运算结果
表示是否有 进位 或 错位
- 结果:有进位或错位;CF=1,CY
- 结果:无进位或错位;CF=0,NC
对于n位的无符号数来说,n-1是最高位
当发生向n位 进位 或 错位时,CF置1
OF_溢出标志
含义:
- 仅对于有符号数而言
结果超出表示范围
- 结果:溢出;OF=1,OV
- 结果:无溢出;OF=0,NV
OF与CF的区别
- CF 是对于 无符号数有意义的 进/错位标志位
- OF 是对于 有符号数有意义的 溢出标志位
带进、借位的加减法
作用:可用于更多位的数值运算
原理:操作数$1\pm$操作数$2\pm$CF
CF进/错位清零的方法:sub ax, ax
在循环自增或自减时,可以通过 inc / dec 指令,不会改变CF标志
用法:与 add
和 sub
相同
加法:adb 操作数
减法:sbb
cmp及 条件转移指令的运用
cmp 使用
本质:做减法,只改变标志位,不改变数值
格式: cmp 统计数值, 比较数值
cmp byte ptr 统计数值, 比较数值
默认格式 cmp word ptr 统计数值,比较数值
原理:
1
cmp ax, dx;计算 ax - dx 根据标志位做出判断
条件转移
含义:见名思意,条件转移
cmp 和 带条件的 jmp 组合使用
条件:
JE/JZ (Jump if Equal/Zero)
- 单词:Jump Equal/Zero
- 含义:如果两个操作数相等(或结果为零),则跳转。
JNE/JNZ (Jump if Not Equal/Not Zero)
- 单词:Jump Not Equal/Not Zero
- 含义:如果两个操作数不相等(或结果不为零),则跳转。
JA/JNBE (Jump if Above/Not Below or Equal)
- 单词:Jump Above/Not Below or Equal
- 含义:如果第一个操作数大于第二个(无符号),则跳转。
JAE/JNB (Jump if Above or Equal/Not Below)
- 单词:Jump Above or Equal/Not Below
- 含义:如果第一个操作数大于或等于第二个(无符号),则跳转。
JB/JNAE (Jump if Below/Not Above or Equal)
- 单词:Jump Below/Not Above or Equal
- 含义:如果第一个操作数小于第二个(无符号),则跳转。
JBE/JNA (Jump if Below or Equal/Not Above)
- 单词:Jump Below or Equal/Not Above
- 含义:如果第一个操作数小于或等于第二个(无符号),则跳转。
JG/JNLE (Jump if Greater/Not Less or Equal)
- 单词:Jump Greater/Not Less or Equal
- 含义:如果第一个操作数大于第二个(有符号),则跳转。
JGE/JNL (Jump if Greater or Equal/Not Less)
- 单词:Jump Greater or Equal/Not Less
- 含义:如果第一个操作数大于或等于第二个(有符号),则跳转。
JL/JNGE (Jump if Less/Not Greater or Equal)
- 单词:Jump Less/Not Greater or Equal
- 含义:如果第一个操作数小于第二个(有符号),则跳转。
JLE/JNG (Jump if Less or Equal/Not Greater)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
* 单词:Jump Less or Equal/Not Greater
* 含义:如果第一个操作数小于或等于第二个(有符号),则跳转。 11. **JC (Jump if Carry)**
* 单词:Jump Carry
* 含义:如果进位标志(CF)被设置,则跳转。 12. **JNC (Jump if Not Carry)**
* 单词:Jump Not Carry
* 含义:如果进位标志(CF)未设置,则跳转。 13. **JO (Jump if Overflow)**
* 单词:Jump Overflow
* 含义:如果溢出标志(OF)被设置,则跳转。 14. **JNO (Jump if Not Overflow)**
* 单词:Jump Not Overflow
* 含义:如果溢出标志(OF)未设置,则跳转。 15. **JS (Jump if Sign)**
* 单词:Jump Sign
* 含义:如果符号标志(SF)被设置,则跳转。 16. **JNS (Jump if Not Sign)**
* 单词:Jump Not Sign
* 含义:如果符号标志(SF)未设置,则跳转。