我是如何学习Java的~灵活的程序跳转方式

14 篇文章 0 订阅
14 篇文章 0 订阅

CPU执行程序是依靠CS:IP来实现的,我们可以通过改变CS和IP的值来灵活的控制我们期望执行的程序。汇编中提供了丰富的转移指令来改变CS和IP的值。

汇编程序中有很多的标号使用,有些标号作为段的地址,如标号code, data。有些标号作为某一段程序的开始,如标号start,标号s。标号的名称可以自己命名。在程序中的标号代表了某一段指令的偏移地址,可以通过 offset 标号 指令来获取标号处指令的偏移地址,如:

assume cs:code
code segment
	start: 
		mov ax, offset start
	s:	
		mov bx, offset s
code ends
end start

虽然可以获取标号的偏移地址,但不把CS:IP指向对应的位置,还是不能控制程序按照期望的来执行,下面来看看汇编中提供了哪些指令来修改CS和IP的值的:

  1. 无条件转移指令 JMP

    jmp short 标号
    

    该指令可以跳转到标号处的位置执行相应的指令,但并不是直接去修改寄存器 IP 的值为标号处的偏移地址来控制跳转的,而是通过指定标号处的偏移地址和当前 IP 中的偏移地址的偏移量来修改 IP 的值从而控制跳转的。

    该指令为段内短转移,可以跳转到偏移量为8位的指令处,所以前后偏移的范围为-128~127个字节。这个偏移量由编译器计算。如:

    assume cs:code
    code segment
    	start:
    		mov ax, 0
    		jmp short s
    		add ax, 2	;此处占用3个字节
    	s:
    		inc ax
    		mov ax, 4c00H
    		int 21H
    code ends
    code start
    

    当CPU执行到 jmp short s 时,IP指向指令 add ax,2 处,当执行 jmp short s 后,会把IP + 3,从而指向 标号s 处开始执行,一直到程序结束。此时ax的值为1。

    为了更好的理解该指令的跳转是靠偏移量来计算IP值的,可以看以下程序:

    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
    

    当程序执行完指令 mov cs:[di], ax,会把 标号s2 处的指令 jmp short s1 写入覆盖 标号s 处的两条指令 nop,在此程序中,jmp short s1 编译后实际指定了 IP 向上的偏移量,同理,在执行完下一条指令 jmp short s 后,IP会指向 标号s 处,此时会执行指令 jmp short s1,而该条指令实际是指定了 IP 向上的偏移量,执行完后,IP根据偏移量实际会指向mov ax, 4c00H处,然后程序退出执行完毕。而s1处的指令根本不会执行。

    jmp near ptr 标号
    

    该指令实现的是段内近转移,和 jmp short 标号 功能相近,都是通过偏移量来跳转的,只是该指令的偏移量为16位,即偏移量范围位:-32768~32767。同样由编译器计算得出。

    jmp far ptr 标号
    

    该指令为段间转移,又称远转移。该指令和上述的段内近转移指令不同,不是通过偏移量来跳转的,而是通过标号处的段地址修改CS,标号处的偏移地址修改IP,从而实现跳转的。如:

    assume cs:code
    code segment
    	start:
    		mov ax, 0
    		mov bx, 0
    		jmp far ptr s
    		db 256 dup(0);该指令表示定义256个字节,内容为0。而数据0000,又可以看成指令,所以此处相当于定义了若干条指令
    	s:
    		add ax, 1
    		inc ax
    code ends
    end start		
    

    除了通过标号来控制程序跳转外,还可以通过寄存器或内存来直接修改CS或IP的值控制程序的跳转:

    jmp 16位寄存器	;段内转移,直接修改IP的值来实现跳转
    jmp word ptr 内存单元地址	;段内转移,通过内存单元中的内容修改IP的值
    jmp dword ptr 内存单元地址 ;段间转移,通过内存单元中高16位的数值作为段地址修改CS的值,低16位的数值作为偏移地址修改IP的值
    

    如:

    mov ax, 0123H
    mov ds:[0], ax
    mov word ptr ds:[2], 0
    jmp dword ptr ds:[0]
    

    执行后,CX = 0, IP=0123H

  2. 有条件转移指令 jcxz

    jcxz 标号
    

    该指令和 jmp 指令类似,同样是段内短转移,通过标号处的偏移量来控制,偏移量范围为:-128~127。

    该指令和寄存器 CX 联合使用,当 CX 中的值为 0 时才会跳转,否则,什么也不做,继续执行下一跳指令:

    assume cs:code, ds:data
    
    data segment
    	db 'abcd', 0
    data ends
    
    code segment
    	start:
    		mov ax, data
    		mov ds, ax
    		mov bx, 0
    	s:
    		mov ch, 0
    		mov cl, ds:[bx]
    		jcxz ok
    		inc bx
    		jmp short s
    	ok:
    		mov dx, bx
    		mov ax, 4c00H
    		int 21H
    code ends
    
    end start
    

    该程序把数据段中的0的偏移地址放入dx,通过在dosbox执行后,可以发现,0处的偏移地址已存入dx中:
    有条件跳转

  3. 循环指令 loop

    loop 标号 ;循环指令,段内短转移,也是通过偏移量来控制转移的,同样是8位的偏移量,范围:-128~127
    
  4. 指令 call

    call 标号
    

    该指令也是通过偏移量来改变IP的值,从而实现转移的,和 jmp 不同的是,该指令会把 IP 的值压入栈中进行保存,相当于进行了以下操作:

    push ip
    jmp near ptr 标号
    

    所以,该指令的可修改的 IP 的偏移量范围:-32768~32767。

    call far ptr 标号
    

    该指令实现了段间转移,在转移之前会把CS和IP的值分别压入栈中进行保存,相当于执行了以下操作:

    push cx
    push ip
    jmp far ptr 标号
    

    同样,call指令也可以通过寄存器和内存来进行跳转:
    通过寄存器来跳转:

    call 16位寄存器
    

    相当于执行了以下操作:

    push ip
    jmp 16位寄存器
    

    通过内存单元来跳转:

    call word ptr 内存单元地址
    call dword ptr 内存单元地址
    

    相当于执行了以下操作:

    push ip
    jmp word ptr 内存单元
    或
    push ip
    jmp dword ptr 内存单元
    
  5. 指令 ret和retf

    ret	;该指令用栈中的数据修改IP的内容,从而实现段内近转移
    reft ;该指令用栈中的数据修改CS和IP的内容,从而实现段间转移
    

    相当于分别执行了以下操作:

    pop ip
    或
    pop ip
    pop cs
    

    该指令通常和call指令联合使用,可以实现类似高级语言中方法调用等功能。
    如:

    assume cs:code
    code segment
    	start:
    		mov ax, 1
    		mov cx, 3
    		call s	;把当前 IP 的值压入栈中,然后跳转到 标号s 处执行,当 s 处指令执行结束后,恢复 IP 的值,开始执行下一条指令:mov bx,ax
    		mov bx, ax
    		mov ax, 4c00H
    		int 21H
    	s:
    		add ax, ax
    		loop s
    		ret	;从栈中弹出值到IP中
    code ends
    end start
    

灵活的跳转方式可以更好的控制程序的执行,通过跳转功能可以使用更加灵活、更加模块化的编程方式来实现我们的功能。一个标号处的代码,相当于一个模块,一个子程序。通过这种方式,可以把多个相互联系、功能独立的子程序组合起来实现一个复杂的实际问题。

目录
上一章
下一章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值