在汇编指令中跳转指令分为两种,一种是无条件跳转指令,一种是有条件跳转指令。
对于前者无条件跳转指令有点类似于高级语言C中的goto语句,goto标志符,无跳转指令的格式也是类似JMP 标号;
对于有条件跳转指令通常都是根据FLAG寄存器的相关状态值SF,OF,AF,PF,CF是否被设置为1或者是0来进行跳转的选择,这个就可以实现相关的分支语句。类似于高级语言中的if等。
(1)无条件跳转指令JMP
基本格式如下:
JMP 标号;
因为在操作系统中我们一般对程序进行分段处理,那么在不同的段就会设置不同的CS寄存器,执行不同指令的过程中实质是设置CS与IP寄存器的值,然后CPU以此来进行指令的取出,由此对于跳转指令我们就分为段内跳转和段间跳转,前者是在一个代码段中,后者是实现不同的代码段的跳转。
首先说一下段间无条件跳转。
段间的无条件跳转的实现原理是:汇编器根据JMP后面设置的标号,计算出标号对应的段内偏移与此时IP寄存器中的值得差值,然后让IP加上该差值,实质就是设置IP的值为该标号对应的段内偏移值。
根据差值所占位的大小我们又分为:无条件段内近转移和无条件段内短转移。
对于前者,偏移与IP的差值大小只占2个字节,后者占1个字节
指令格式分别如下:
无条件段内短转移:JMP SHORT 标号
无条件段内近转移:JMP NEAR PTR 标号
对于这个PTR什么时候需要添加我也不是很清楚,到后面的学习过程中明了后在进行修改。
对于无条件转移指令,此时的IP值是通过标号直接设置的,在汇编器解析的时候进行设置,但是有时候我们可以把需要设置的IP值放到通用寄存器或者是存储器中,那么这样就可以实现无条件段内间接跳转指令。
指令的格式如下:
JMP OPRD
其中OPRD可以是通用寄存器,也可是存储单元,寻址方式除了是立即数寻址外,可以是其它的寻址方式。
例如:
JMP AX;JMP [AX];JMP WORD PTR [1234H]等
(2)无条件段间跳转
所谓的无条件段间跳转就是通过相关的操作直接设置CS和IP寄存器的值,使得执行不同代码段中的代码
指令格式和上面的大同小异,但是汇编器在进行解析的时候会设置CS和IP的值。
指令分为两种:一种是直接跳转
JMP FAR PTR 标号
一种是间接跳转,通过直接寻址的方式,把存储器中的低字放到IP中,高字放到CS中,指令格式如下
JMP DWORD PTR OPRD
例如:JMP DWORD PTR [1234H],则执行后IP = DS*16+[1234H],CS = DS*16+[1236H]
总结了一些JMP跳转指令的相关格式:
格式 | 描述 | 举例 | 类别 | 说明 |
jmp 16位寄存器 | 以16位寄存器的值改变IP | jmp ax | 段内转移 |
|
jmp 段地址:偏移地址 | 以立即数改变段地址和偏移地址 | jmp 0045H:0020H | 段间转移 |
|
jmp short 标号 | 以标号地址后第一个字节的地址来改变IP,实际上这个功能可以作如下描述: | jmp short sign | 段内短转移 | 对IP的修改范围是-128->127,实际算法是编译器根据当前IP指针的指向来计算到底偏移多少个字节来指向下一条指令,下面这段代码就会出编译错误 |
jmp near ptr 标号 | 以标号地址后第一个字的地址来改变IP, | jmp near ptr sign | 段内近转移 | 对IP的修改范围是-32768->32767 |
jmp far ptr标号 | 以标号的段地址和指令地址同时改变CS和IP | jmp far ptr sign | 段间转移 |
|
jmp word ptr 内存地址 | 以内存地址单元处的字修改IP,内存单元可以以任何合法的方式给出 | jmp word ptr ds:[si] | 段内转移 |
|
jmp dword ptr内存地址 | 以内存地址单元处的双字来修改指令,高地址内容修改CS,低地址内容修改IP,内存地址可以以任何合法的方式给出 | jmp dword ptr [bx] | 段间转移 | s1 segment |
对于JMP 段地址:偏移地址,并不是所有的MASM都支持的,需要依据实际的形式来判断。
上面我们看到了段内的无条件跳转指令,但是和很多高级语言进行对比,我们在很多时候都是通过条件的判断来决定是否需要进行跳转,同样在汇编指令中也提供了相关的条件跳转指令,我们现在一一进行介绍:
明确一下,在汇编指令中N代表的是否。同时进行条件跳转的指令都是段内跳转,因此有短和近跳转了撒!
(1)根据标识FLAG寄存器来判断是否需要进行跳转
我们根据前面的需要知道相关的算术运算、逻辑运算、移位运算(部分指令会影响CF)都会影响FLAG寄存器中的部分标识位的值,那么根据这些标志位我们可以判断是否需要进行跳转,譬如CMP AX,BX是判断AX和BX的大小,那么通过相关设置的标志位我们就可以判断是AX大还是BX大,然后决定是否需要转移嘛,这就是所谓的分支语句了撒!
对于有符号数分大于(G-great),等于(E-equal),小于(L-light)
对于无符号数分为大于(A),等于(E),小于(B)
根据标志位跳转的指令:
跳转相关的标志位:
11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|
OF | DF | IF | TF | SF | ZF | AF | PF | CF | |||
溢 出 | 符 号 | 零 | 未 用 | 辅 助 | 未 用 | 奇 偶 | 未 用 | 进 位 |
通过上面的跳转指令我们就可以实现简单的分支和循环,例如
MOV CX,10H
NEXT:
........
DEC CX
JNZ NEXT
实现的是执行NEXT中的代码段10次
但是通过自己手动的写相关的循环语句有时候很复杂,增加了编码的难度,因此在汇编指令中有了如下的专门的循环指令,如下所示:
LOOP = CX不为零的时候进行跳转
LOOPE/LOOPZ = CX不为零并且相等的时候跳转
LOOPNE/LOOPNZ = CX不为零并且不相等的时候跳转
LCXZ CX为零的时候跳转
通过相关的循环+跳转语句就可以实现高级语言中的分支语句和循环语句的执行了