下面想就自己对一条汇编指令的学习来展开一下学习汇编的一点小小的体会。。。
例子:JMP NEAR PTR 指令
1、找到指令缩写单词的原形
相信有些刚开始学习汇编的人都有和我一样的第一印象,就是对汇编指令采用英文单词缩写觉得不适应。。。是呀,要是仅仅强记这些缩写,未免有点牵强,而且容易对自己刚准备入门的学习心态造成一定的打击。最好的办法是能够找到这些缩写所对应的英文单词,这样就容易记忆了,而且心里也没有强记的那种抵触。况且培养一定的英文基础对于以后更加深入学习COMPUTER应该还是会有帮助的吧。
JMP是JUMP的缩写,直译是“跳”
NEAR是单词,直译是“邻近的”
PTR是POINTER的缩写(对于这一点不是很确定,但我在网上能找到能理解的也就是这个了),直译是“指示器”
怎么样,要是结合上面这些缩写单词的原形再来看这条 JMP NEAR PTR 汇编指令,会不会觉得舒服多了?
2、不被表面文字所拘束
JMP NEAR PTR 是一条段内近转移指令。要理解这条指令的含义,首先就需要对指令描述中的“段”有清楚的理解。汇编最开始引入“段”好像就是从讲8086CPU对内存的寻址开始的,而且从提出“段”的概念开始就不断强调“段”定义的灵活性。。。那么JMP NEAR PTR这条指令描述中的“段内”所针对的“段”到底是什么段呢?如果仅仅理解为自己所编程序的代码部分,那可能对后面理解指令的实现机理还是有一定束缚的。如果结合指令讲解中的“IP的位移”以及“位移范围:-32768~32767”反过来理解指令描述中的“段内”的含义,或者通过编写一段小代码在DEBUG里过一下,就能进一步明确:JMP NEAR PTR 指令描述中的“段”的确“覆盖”了程序代码部分,但它更是因为融合了内存寻址中“64K”最大段空间的理念所以能够实现代码段内任意地址寻址的典范。
3、探索指令本身的实现机理
对于 JMP NEAR PTR 为什么能够实现代码段内任意地址向段内其它所有地址转移,实际上是和 NEAR PTR 所指明的16位(字型)位移数据 AND 8086基于“段”的寻址方式 AND 16位的IP寄存器分不开的。那么又为什么对 JMP NEAR PTR 的位移范围反复强调是“-32768~32767”而不是“0~65535”呢?反正都是段内循环寻址模式,为什么就不可以是“0~65535”呢?^_^。。。这个细节问题我没有确切答案,但我不认为这是因为受硬件限制或者仅仅“就是设计成这样”,这样的设计理念更像是基于某种对运算过程的考量而进行的架构。当然这只是作为自己的一点推测,也不准备在这里再多妄言什么了。重点是能够从类似这样的探索过程中不断地提出问题而引发更多的联想,而且我相信,象这样钻牛角尖的问题应该不会在看雪这样的技术论坛里被BS。。。
写在最后:自己有的东西也就是瞎琢磨,不一定对,也不一定准确。但仅仅从一个 JMP NEAR PTR 指令就引发出这么些内含的可供思考的DIRECTIONS,不禁深深感慨:世界也许比自己原来想象的要大很多很多啊。。。没事来看雪看看是因为被这里所渲染的技术氛围吸引,希望能在这里多感受一些。即使以后哪一天放下汇编放下技术,我也应该不会忘了在这里还有这么一个那么让人羡慕的坚持技术的圈子吧。。。
是啊,不看不知道,一究吓一跳.
原来JMP NEAR PTR 可以在以改指令的地址-32768或者+32767(就是一个'段')内顺意跳啊.原因大概是JMP NEAR PTR 转译成机器码时会转化成相对位移量,而这个位移量只给2个字节的空间,而且还是有符号的(要表示是向上跳还是向下跳)...
看看助记符,再看看机器码,感觉光看汇编不行啊,还要再深入啊!
看来技术没极限啊...
其实JMP NEAR PTR还是在“假定”最大代码段 CS:0~CS:0FFFFH(也就是CS:0~CS:65535)的范围内跳来跳去找程序里标号处的地址。。。但因为标号又必须是明文写在代码部分里的,所以段其实就是代码段,段内其实就是指代码段内。。。不要误解以IP位移以“-32768~32767”为范围就有可能跳到CS:0上面或CS:0FFFFH下面的内存空间。。。IP累加位移后还是赋值于IP本身的,而IP本身应该是作为无符号数处理,所以没可能在以 CS:IP 所指向指令JMP NEAR PTR的上下内存空间随便跳的。。。段还是代码段,段内还是指代码段内,我写的第2点只是希望能深究一下这个代码段的由来,要是在‘段’的问题上误导了别人,可就罪过了。。。
躺在床上,反反复复还是想着自己在主贴对 JMP NEAR PTR 指令第2点体会上的文字表达上的不达意以及第3点体会上对‘-32768~32767’理解的不到位。。。
对于第2点,也就是 JMP NEAR PTR 的跳转范围问题,结合上在4楼的回帖就应该算是完全表达了自己对这个问题的理解了。。。
但是对于第3点,我想来想去的结果是还有一种可能:也许为什么位移是‘-32768~32767’而不是‘0~65535’的问题有点想偏了。。。把‘-32768~32767’和 JMP NEAR PTR 联想得那么“紧密”以致于非要求证出‘-32768~32767’对 JMP NEAR PTR 这条指令究竟有什么更深层次的联系根本就是一个原则性的错误。。。‘-32768~32767’的说法应该是内含了两层不同意义上的含义:
1、为什么非要把位移看成有符号数?强调编译程序在处理位移单元参与IP地址修改时的灵活性。编译程序对于最后决定IP地址转移的位移起到了判别类型和最终确定的重要作用,所以能够“向上”转移(即位移可以为负)的说法实际上暗示了在对程序的编译处理层面上编译器因为要最优处理位移值所起到的重要判别作用。是编译器先选择确定好的IP位移值,然后程序运行执行到转移指令时,IP位移值才进入CPU数据处理环节,此时所进行的运算并没有区别于其他运算的更深层次的含义。所以对“IP位移值为什么是有符号数”问题探究的关键不应放在‘-32768~32767’与 JMP NEAR PTR 这条具体指令有什么特殊联系的问题上。关键应该在于汇编编译器是怎么样判别、处理IP位移值,又是根据什么最优化的依据来确定IP位移值的问题。。。
2、为什么要在有符号数的角度说明转移位移的范围?仅仅是因为在上面1的理由后,继续站在在有符号数角度说明了 JMP NEAR PTR 对IP位移修改的范围。。。重点在于强调出这个范围的大小,而不是范围的起点和终点。。。
总之一句话吧,我认为对于为什么是‘-32768~32767’而不是‘0~65535’的问题的讨论更多地不是放在CPU或者CPU指令的架构层面上,而是应该在放在汇编编译器对程序的处理层面上。。。
但是介绍MASM编译过程中的具体行为之处,特别是对于 JMP NEAR PTR 对除正常IP位移量之外的数据的处理模式,却没有看到一个明显支持“-32768~32767”的说法。
所以对于为什么一定要强调“-32768~32767”的问题进一步保留。。。而且由此可以引发联想的一件事情就是:假如我以后有足够的反编译知识和能力,一定考虑把MASM给逆向了,到时候好好看看它的代码,看看这家伙本身的编译流程对 JMP NEAR PTR 的处理到底是个什么样子?为什么书上会反复强调“-32768~32767”?
强烈预感:“-32768~32767”并不是MASM对于 JMP NEAR PTR 指令处理过程中的必须表达,“-32768~32767”只是因为契合补码运算原理所展示出来的虽然真实但是比较容易误导我这样的读者对MASM运行机理理解的一个JOKE。“-32768~32767”作为描述实际数据处理的CASE肯定是没有错的,但是“-32768~32767”一定可以换一种面目以一种更接近真实运算架构的面目展示出来。。。
2、对于我提出的问题,也就是“-32768~32767”到底能从哪里找到可靠的依据来证明它是对JMP NEAR PTR指令处理过程中必须或者说不可替代的表达,讨论这个问题的实践基础只有可能来自两个方面:
一、MASM编译器本身的源程序。
二、经过MASM编译过的程序。
对于第一个方面,根据编译器本身的源代码到底是怎么计算IP偏移数据来说明问题肯定是最直接也是最有说服力的。可惜我没有足够的能力看懂MASM编译器的源代码,所以只能考虑第二个方面,也就是通过分析经过MASM编译过的程序来推测MASM编译器的编译原理。但在分析了一些简单的小程序后,我突然意识到一个问题:也就是CPU处理类似“IP=IP+位移量”的过程中,CPU本身也许根本不会去区分所计算的位移量到底是有符号数还是无符号数,也就是说CPU更加不会去分辨IP位移量是正数还是负数。也就是说,假如我们看到一条指令是 “JMP FF00”,那么不仅CPU不知道,就是我们也根本无法仅仅根据“FF00”就去反推MASM编译器是怎么把它计算出来的,不知道它到底是代表“+65280”还是“-256”。这就是说,利用编译器编译后的程序来推测编译器本身的运行原理是远远不够的。。。这也就是,“-32768~32767”到底是不是必须的不可替代的对IP位移范围的说明是没有办法在编译过后的程序里找到确凿的答案的。。。这也就是说,只有MASM编译器本身的源程序才能对这个问题给出实例证明。
3、在无法直接找到对“-32768~32767”不可替代性的实例证明的情况下,我们不妨在回头看看课本中到底是怎么引出“-32768~32767”的说法的,可以以此作为一种辅助材料重新地不在实例上而是在逻辑上继续进行我们的推测。事实上,我能在汇编书里找到的是,对MASM如何计算JMP NEAR PTR指令IP位移量的说明仅仅是有向前和向后转移的这种表述,但并没有说编译处理过程中向后转移的位移就不能超过“32767”,向前转移的位移就不能超过“-32768”。“-32768”和“32767”从来没有被独立地列出,而只是作为一个范围“-32768~32767”列出过,这个范围仅仅是从有符号数角度确定IP位移所占字节数而使用的。另外,书上对MASM编译器怎么计算IP位移的公式明显更不支持“-32768~32767”的说法。所以我才推测:“-32768~32767”只是从某一个特定的角度来告诉我们IP位移的范围,但作为更加深入地理解或者说推测JMP NEAR PTR 指令的编译过程,我们更应该从MASM编译器对IP位移的原始计算过程和数据表达的具体含义的角度来理解这个问题。
楼主不妨用JMP或者JMP SHORT指令来测试位移玲是否是有符号数,JMP NEAR PTR用2个字节来放,而16位的偏移也正好2个字节来放,因为是采取补码形式,只从JMP NEAR PTR看机器码也确实难以去疑,尤其是在已怀疑的情况下(已有先入为主的倾向性意见),建议: 上跳和下跳近点和参考 JMP 或JMP SHORT的对应的机器码来看,应该能确定是有无符号数。
诚如所说,测试了JMP SHORT,发现CPU对字节型的偏移地址运算的确是当作有符号数对待的(因为只有直接在该字节型数据的更高两位补FF然后参与IP运算才有可能得到正确的结果)。但对于MASM直接给出的字型的偏移地址(也就是大多数 jmp near ptr 指令所涉及的),CPU是直接进行运算,已经不会去判断数值本身所代表的是什么类型的数。我们也不能简单地推测MASM对 JMP NEAR PTR 的处理也应该和 JMP SHORT 一样,是把IP位移量当作有符号数而给出的。实际上,我认为,在针对部分程序的时候,把这个位移量理解成有别于“-32768~32767”范围的数也许更符合这个IP位移量的来由,更能够帮助我们理解MASM计算IP位移值的机理。
mov ax,0
jmp near ptr s
db 0ff00H dup (0)
s:
mov ax,0
mov ax,4c00H
int 21H
code ends
end start
MASM汇编器在处理它的时候,编译器顺序扫描并由内部的地址计数器得到 jmp near ptr 指令处的地址aj(扫描过程涉及一开始对jmp near ptr的SHORT处理,但没有关系,因为扫描完成后会回归的正确数值)以及标号s处的地址as,然后根据IP位移量的计算公式“as-aj”计算出从 jmp near ptr 到s标号处的IP偏移地址0FF00H。那么针对这个源程序和编译的过程,我们再看一下到底是“-256”(向前转移256个字节)还是“+65280”(向后转移65280个字节)更能合理地说明这个IP位移值的由来?“+65280”作为能够直接说明编译器对 jmp near ptr 这条指令涉及的IP位移量的编译原理性阐述显然并不在“-32768~32767”这种仅仅站在狭义的有符号数角度去说明位移范围的范围内。虽然作为限制成字型数据的纯数字来讲,“+65280”==0FF00H==“-256”的确成立,但是作为理解真实的IP位移值的由来来说,我们人为地仅仅根据数据的表象就建立起某些“等号”而不深入探讨“等号”外延的行为是要为之付出代价的。。。特别对于新入门的象我这样有时候爱钻牛角尖的PEOPLES。。。