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