转移指令分类
- 无条件转移 JMP
- 段内转移:只修改
IP,如jmp ax - 段间转移:同时修改
CS:IP,如jmp 1000:1
- 段内转移:只修改
- 近转移:
IP变化范围-32768~32767 - 短转移:
IP变化范围-128~127 - 条件转移
- 循环 LOOP
- 过程
- 中断
操作符 offset
获取标号的偏移地址
start:mov ax,offset start ; 等价于 mov ax,0s:mov ax,offset s ; 等价于 mov ax,3
mov ax只占 1 个字节,剩下两个字节为0000H(offset start),所以s的偏移地址是3
复制两字节的指令
assume cs:codecode segments:mov ax,bxmov si,offset smov di,offset s0mov ax,cs:[si]mov cs:[di],axs0:nopnopcode endsend s
指令跳转 jmp
short
jump 指令的机器码为EB + 8位偏移地址(IP)差
assume cs:codecode segments:mov ax,100mov ax,100s0:mov ax,100jmp short s ; 跳转至 ss1:mov ax,100jmp short s2 ; 跳转至 s2mov ax,100s2:mov ax,100mov ax,4c00Hint 21Hcode endsend s
执行jmp前的IP为下一条指令的IP,因此
由于是短转移,范围为
-128~127,因此00~7f表示偏移0~127个字节,ff~80表示偏移-1~-128偏移使用补码表示,即-128的补码为10000000B``80H
near ptr
近转移,转移值也是补码表示,
EP + 16位偏移差
assume cs:codecode segments1:mov ax,100jmp near ptr s2mov ax,100; 此处省略一堆代码s2:mov ax,100mov ax,4c00Hint 21Hcode endsend s1
far ptr
远转移,段间转移,
EA + 转移地址
assume cs:codecode segments1:mov ax,100jmp far ptr s2dw 256 dup (0)s2:mov ax,100mov ax,4c00Hint 21Hcode endsend s1
dword
根据两个字(32位,4字节)进行转移,高位等于
CS,低位等于IP
mov ax,1234Hmov ds:[0],ax ; 代表低位mov word ptr ds:[2],0 ; 代表高位jmp dword ptr ds:[0]
奇怪的代码
assume cs:codecode segmentmov ax,4c00Hint 21Hstart:mov ax,0s:nopnopmov di,offset smov si,offset s2mov ax,cs:[si]mov cs:[di],axs0:jmp short ss1:mov ax,0int 21hmov ax,0s2:jmp short s1nopcode endsend start
这段代码其实是把s2的代码放在s的两个nop上,即
而EBF6实际上是相对于000A往前F6个字节,即0000
条件判断 jcxz
当cx=0时跳转到指定地址
; 在 2000 段中查找第一个为 0 的字节的偏移地址, 并送入 dx 中assume cs:codecode segmentstart:mov ax,2000Hmov ds,axmov bx,0; 将前 6 个数据变成非零mov word ptr ds:[0],1020 ; fc 03mov word ptr ds:[2],1020mov word ptr ds:[4],1020mov ch,0 ; [bx] 是字节型数据, 因此使 ch=0s:mov cl,[bx]jcxz okinc bxjmp short sok:mov dx,bxmov ax,4c00Hint 21Hcode endsend start
循环 loop
当cx≠0时跳转到指定地址
; 使用 loop 实现上述功能assume cs:codecode segmentstart:mov ax,2000Hmov ds,axmov bx,0mov word ptr ds:[0],1020mov word ptr ds:[1],1020mov word ptr ds:[2],1020mov ch,0s:mov cl,[bx]inc cx ; 因为 loop 先将 cx-1 再判断是否为 0 才退出, 所以这里使 cx+1inc bx ; 在退出循环前 bx+1 了loop sok:dec bx ; 因此这里要把 bx-1mov dx,bxmov ax,4c00Hint 21Hcode endsend start
ret/retf
ret
相当于(IP)=((ss)×16+(sp))``(sp)=(sp)+2即pop IP
数据出栈后赋值给
IP寄存器 近转移
assume cs:codestack segmentdb 16 dup (0)stack endscode segmentmov ax,4c00H ; 076A:0000int 21Hstart:mov ax,stackmov ss,axmov sp,16mov ax,0push ax ; 0000mov bx,0ret ; pop ip(0000) 而 cs 默认就是 076Acode endsend start
retf
相当于(IP)=((ss)×16+(sp))``(sp)=(sp)+2``(CS)=((ss)×16+(sp))``(sp)=(sp)+2即pop IP``pop CS
数据出栈后赋值给
IP``CS寄存器 远转移
assume cs:codestack segmentdb 16 dup (0)stack endscode segmentmov ax,4c00H ; 076A:0000int 21Hstart:mov ax,stackmov ss,axmov sp,16mov ax,0push cs ; 076Apush ax ; 0000mov bx,0retf ; pop ip(0000) pop cs(076A)code endsend start
call
近转移、远转移 call 指令会对下一条指令的内存地址进行保存(入栈)
call 标号
相当于(sp)=(sp)-2``((ss)×16+(sp))=(IP)``IP 变成标号 IP即push IP``jmp near 标号
机器码E8 + 偏移差
assume cs:codestack segmentdb 16 dup (0)stack endscode segmentstart:mov ax,0call s ; push 0006 jmp 0007inc axs:pop ax ; ax=0006mov ax,4c00Hint 21Hcode endsend start
call far ptr 标号
相当于push CS``push IP``jmp far ptr 标号
机器码9A + IP + CS
assume cs:codestack segmentdb 16 dup (0)stack endscode segmentstart:mov ax,0call far ptr sinc axs:pop axadd ax,axpop bxadd ax,bxmov ax,4c00Hint 21Hcode endsend start
call IP
相当于(sp)=(sp)-2``((ss)×16+(sp))=(IP)
assume cs:codestack segmentdb 16 dup (0)stack endscode segmentstart:mov ax,6call axinc axpop axmov ax,4c00Hint 21Hcode endsend start
call word ptr […]
[...]里的数值当作跳转的IP,同理,入栈的是下一条指令的IP
assume cs:codestack segmentdb 16 dup (0)stack endscode segmentstart:mov ax,0123hmov ds:[0],axcall word ptr ds:[0]mov ax,4c00Hint 21Hcode endsend start
call dword ptr […]
[...]第一个字为IP第二个字为CS但是入栈是CS先入
assume cs:codestack segmentdb 16 dup (0)stack endscode segmentstart:mov ax,0123hmov ds:[0],axmov word ptr ds:[2],0call word ptr ds:[0]mov ax,4c00Hint 21Hcode endsend start
子程序(类似线程)
assume cs:codestack segmentdb 16 dup (0)stack endscode segmentmain:mov ax,1mov cx,3call sub ; 将当前 IP 保存起来, 并跳转至子程序 submov bx,axmov ax,4c00Hint 21Hsub:add ax,axloop subret ; 执行完 sub 后重新跳转回 call 后面的指令code endsend main
模块化设计
子程序带参数
assume cs:codedata segmentdw 1,2,3,4,5,6,7,8dd 0,0,0,0,0,0,0,0data endscode segmentmain:mov ax,datamov ds,axmov si,0mov di,16mov cx,8s:mov bx,[si] ; 取出要计算的值保存在 bx 中call cubemov [di],ax ; 将计算的结果低位放到 [di] 中mov [di].2,dx ; 将计算的结果高位放到 [di].2 中add si,2add di,4loop smov ax,4c00hint 21h; 说明:计算 N 的 3 次方; 参数:(bx)=N; 结果:(dx:ax)=N^3cube:mov ax,bx ; 从 bx 中取出要计算的参数mul bxmul bxretcode endsend main
批量参数
将参数放在内存中,只需要使用寄存器传入第一个参数的内存地址
assume cs:code,ds:datadata segmentdb 'sjashdeidede'data endscode segmentstart:mov ax,datamov ds,axmov si,0 ; 第一个参数的偏移地址mov cx,12 ; 循环 12 次call capitalmov ax,4c00hint 21hcapital:and byte ptr [si],11011111B ; 将字符串变成大写inc siloop capitalretcode endsend start
使用 jcxz 优化
data segmentdb 'sjashdeidede',0 ; 末尾 0 表示字符串结束, 因此不需要传入字符串长度data ends
capital:mov cl,[si]mov ch,0jcxz okand byte ptr [si],11011111Binc sijmp short capital; loop capital 也行ok:ret
由于capital中使用了寄存器cx因此这个程序要求主程序start中不能使用cx
使用栈优化
主程序的cx可以使用栈保存起来
assume cs:code,ds:datadata segmentdb 'word',0db 'unix',0db 'wind',0db 'good',0data endscode segmentstart:mov ax,datamov ds,axmov bx,0mov cx,4s:mov si,bxcall capitaladd bx,5loop smov ax,4c00hint 21hcapital:push cx ; 保存循环次数push si ; 保存数据偏移change:mov cl,[si]mov ch,0jcxz okand byte ptr [si],11011111Binc siloop changeok:pop sipop cxretcode endsend start
