购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

5.6 使程序进入无限循环状态

数字显示完成后,原则上整个程序就结束了,但对处理器来说,它并不知道。对它来说,取指令、执行是永无止境的。程序有大小,执行无停息,它这么做的结果,就是会执行到后面非指令的数据上,然后……

问题在于我们现在的确无事可做。为避免发生问题,源程序第98 行,安排了一个无限循环:

jmp 是转移指令,用于使处理器脱离当前的执行序列,转移到指定的地方执行,关键字near表示目标位置依然在当前代码段内。上面这条指令唯一特殊的地方在于它不是转移到别处,而是转移到自己。也就是说,它将会不停地重复执行自己。不要觉得奇怪,这是允许的。

处理器取指令、执行指令是依赖于段寄存器CS 和指令指针寄存器IP 的,8086 处理器取指令时,把CS 的内容左移4 位,加上IP 的内容,形成20 位的物理地址,取得指令,然后执行,同时把IP 的内容加上当前指令的长度,以指向下一条指令的偏移地址。

但是,一旦处理器取到的是转移指令,情况就完全变了。

很容易想到,指令jmp near infi 的意图是转移到标号infi 所在的位置执行。可是,正如我们前面所说的,程序在内存中的加载位置是0x0000:0x7C00,所以,这条指令应当写成

实际上,不加还好,加上了0x7C00,就完全错了。

jmp 指令有多种格式。最典型地,它的操作数可以是直接给出的段地址和偏移地址,这称为绝对地址。比如:

此时,要转移到的目标位置是非常明确的,即,段地址为0x5000,段内偏移地址为0xf0c0。在这种情况下,指令的操作码为0xEA,故完整的机器指令是:

处理器执行时,发现操作码为0xEA,于是,将指令中给出的段地址传送到段寄存器CS;将偏移地址传送到指令指针寄存器IP,从而转移到目标位置处接着执行。

但是,在此处,jmp 指令使用了关键字“near”,且操作数是以标号(infi)的形式给出。这很容易让我们想到,这又是另一种形式的转移指令,转移的目标位置处在当前代码段内,指令中的操作数应当是目标位置的偏移地址。实际上,这是不正确的。

实际上,这是一个3 字节指令,操作码是0xE9,后跟一个16 位(两字节)的操作数。但是,该操作数并非目标位置的偏移地址,而是目标位置相对于当前指令处的偏移量(以字节为单位)。在编译阶段,编译器是这么做的:用标号(目标位置)处的汇编地址减去当前指令的汇编地址,再减去当前指令的长度(3),就得到了jmp near infi 指令的实际操作数。也不是编译器愿意费这个事,这是处理器的要求。这样看来,jmp near infi 的机器指令格式和它的汇编指令格式完全不同,颇具迷惑性,所以一定要认清它的本质。这种转移是相对的,操作数是一个相对量,如果你人为地加上0x7C00,那反而不对了。

那么,编译器是如何区分这两种不同的转移方式呢?很简单,当它看到JMP 之后是一个绝对地址,如0xF000:0x2000 时,它就知道应当编译成使用操作码0xEA 的直接绝对转移指令。相反地,如果它发现JMP 之后是一个标号,那么,它就会编译成使用操作码为0xE9 的相对转移指令。关键字“near”不是最主要的,它仅仅用于指示相对量是16 位的。

在这里,目标位置就是当前指令自己的位置,间隔的长度为0。再用0 减去当前指令长度3,这是一个负数。打开Windows 计算器程序,实际减一下看看,你会发现,用二进制的0 减去二进制的11,结果是

由于是在不断地向左边借位,除了最右边是01 外,左边都是无休止的“1”。

再切换到十六进制计算一下0x0 减去0x3,结果是

同样由于是在不断地向左边借位,除了最右边是D 外,左边都是无休止的F。

由于在指令中使用了near 关键字,因此,以上无休止的结果将被截断,只保留右边16 位,即0xFFFD。又因为x86 处理器使用低端字节序,所以,jmp near infi 指令编译后的机器代码为 E9 FD FF。

你可能觉得疑惑:0xFFFD 等于十进制数65533,而这条指令需要的操作数实际是负3,我们这样做的原理是什么呢?计算机又是怎么表示负数的呢?不要着急,下一章我们就要介绍负数,并回过头来重新认识这个问题。

在指令执行阶段,处理器用指令指针寄存器IP 的内容加上该指令的操作数,再加上该指令的长度(3),就得到了要转移的实际偏移地址,同时CS 寄存器的内容不变。因为改变了IP 的内容,这直接导致处理器的指令执行流程转向目标位置。

就jmp near infi 指令来说,当它执行时,转移到的目标位置是IP+0xFFFD+3。用Windows计算器程序实际做一下,0xFFFD+3 的结果是0x10000,但处理器只使用16 位的偏移地址,故只保留16 位的结果0x0000。因此传送到指令指针寄存器IP 的内容依然是它原来的内容,这导致处理器再次执行当前指令。

jmp 指令具有多种格式,我们现在所用的,只是其中的一种,叫做相对近转移。有关其他格式,以及这些格式之间的差异,我们将在后面的章节里结合具体的实例进行讲解。

检测点5.4

写出以下程序片断中那两条JMP 指令的机器指令码,并在NASMIDE 中编译验证你的答案是否正确:

jmp near start ( )

data db 0x55,0xaa

start: mov ax,0

jmp 0x2000:0x0005 ( ) tK8gi+D/NL06+rBBDEAki3LEfm6Rw5d1wkNBHakcD3b6z1PtFu1N3vvUhJciCxUH

点击中间区域
呼出菜单
上一章
目录
下一章
×