出处:http://www.osix.net/modules/article/?id=595
说明:虽然超简单,竟也被我奇迹般的译的错误百出,只望大侠们一笑.
译者:aalloverred
数据传送指令深窥
数据传送(及拷贝)汇编语言中应被充分理解的一项基本技术.本文将深入的解读
我们如何在程序内部来回传送数据,以及如何做到高效.
尽管本文是为新手写的,但是熟练的程序员也可借此重温已有技术.文后还有小测
验可以测试你新学到的技能.
-----------------------
汇编语言-基本指令
数据传送指令深窥
-----------------------
作者 Giovanni Tropeano 于11/2004
为 OSIX 而作
<<文章目录<<
...前言
...数据拷贝-必要动作
...测测你学到了什么
:::前言:::
这是我为OSIX做的第3篇文章,而且我已经开始喜欢上了写教程这个东东.这次我
们将详细的复习汇编语言中用于传送数据的基本指令.
读完本文之后,你将学会:
如何在内存和CPU寄存器之间拷贝数据
如何在寄存器之间拷贝数据
如何使用xchg指令
传送数据的操作码
MOV的限制
本文针对新手,但熟练的汇编程序员也可能从中学到些东西.
现在开始...
::: 数据拷贝-必要动作 :::
几乎所有的程序都会将数据从一个地方拷贝到另一个地方.使用汇编语言时,它是
通过使用MOV指令完成的.每个MOV指令都有如下的形式:
MOV 目的,源
它将一个字节,字或双字从源操作数地址拷贝到目的操作数地址.原地址的数据不
会变化.而目的和源必须大小相同.MOV指令与高级语言中的赋值表达式相似,例如
在Visual Basic中:
myNumber = 128
myVariable = myNumber
在汇编中的对应形式会象下面这样:
mov myVariable, ecx ;myVariable = myNumber
(假设ECX包含myNumber中的数据,而myVariable双字大小,位于内存中)
可是再复杂些这种类比就会不成立,比如下面的例子:
myVariable = 3 * myNumber + 1
就不可能只用一个MOV指令标示出来.事实上,要完成这句需要几行指令(稍候我们
再实现).在将结果拷贝到目的地之前需要用乘法指令计算右边表达式的值.
80x86处理器中使用MOV指令有些限制.其中一条就是不能由内存拷贝到内存,比如
下面的做法就是不正确的,
这将产生错误:
mov 变量1, 变量2
如果仔细观察一下,你会发现传送字和传送双字的MOV指令的操作码是相同的。
的确,80x86处理器为每个活动的段保存一个段描述,其中有一位就是用来决定操
作数默认是16位还是32位的.这位为1表示操作数是32位的.所以比如 B8 操作数
意思是将操作数中双字立即数拷贝到EAX,而不是将字立即数拷贝到AX.如果你编
写16位指令 mov ax,0 那么编译器将在目标码前插入字节66,实际生成为66 B8
0000.一般讲,前缀66告诉编译器,对于前缀后的那条指令,操作数要由默认的大小
(32位或16位)转而使用另一种大小(16位或32位).
/*你究竟是怎么知道这些的,Trope(作者)?!?试试就知道了.*/
有时候指令会影响标志寄存器中的某些位.一条指令可能有三种影响:
没有标志变化
某些特定的标志根据指令的结果变为某个特定值
一些指令会发生变化,但它们的值无法预测
所有的mov指令都属第一类,即任何mov指令都不会改变任何标志.
时刻都记住重要的一点,即一些你认为好像合乎逻辑的事情恰恰是mov指令办不到
的.其中就有下面这些指令:
传送的源和目的都在存储器内
立即数传送到段寄存器
由标志寄存器中传入或传出
传送到指令指针寄存器
由一个段寄存器传送到另一个段寄存器
传送的操作数大小不相等
几个对象一同传送
那要是真需要执行这些操作怎么办呢?好问题!让我们解决其中一些.
尽管没有mov指令实现由内存到内存的传送,我们可以用下面的方法实现.比如,
不正确的内存到内存传送
mov Count, Number ; 两个内存操作数是不正确的
但是我们可以用下面的方法达到这个目的:
mov eax, Number ; Count := Number
mov Count, eax
每条指令都是用了寄存器EAX作为一个操作数.你也可以使用其他的可用的寄存
器.
现在,如果想要将一个立即数载入到段寄存器,可以这样实现:
使用一个16位Mov将立即数传送到寄存器
再使用一个mov将16位寄存器传送到段寄存器
要将数据由字大小变为字节大小,如下面的做法就是合理的,即将字传入16位寄存
器,然后仅将其高字节或低字节传送到目的地.反过来,我们也可以16位寄存器的
高低字节合在一起,将产生的结果字传送到某个目的地.
比如已经将源地址和目的地址声明为了
source DWORD 4 DUP(?)
dest DWORD 4 DUP(?)
要将四个双字由源传送到目的,一种方法就是使用四条指令
mov dest, source ; 拷贝第一个双字
mov dest+4, source+4 ; 拷贝第二个双字
mov dest+8, source+8 ; 拷贝第三个双字
mov dest+12, source+12 ; 拷贝第四个双字
形如source+4的地址指向地址source后的四个字节(一个双字)处.因为四个双字
在内存中是连续存储的,所以source+4指向第二个双字.
下面要说一个功能强大的命令,汇编中的XCHG指令.它能由一条指令完成高级语言
中3,4指令才能完成的功能.
现假设要交换Value1和Value2的值.在高级语言中会由类似下面的方法实现:
Temp := Value1; { 交换 Value1 和 Value2 }
Value1 := Value2;
Value2 := Temp;
假定Value1在EAX中,Value2在EBX中,要实现与上面相同的功能可以这样:
xchg eax, ebx ; 交换 Value1 和 Value2
也可以不使用xchg这样实现:
模仿XCHG指令
mov ecx, eax ; 交换 Value1 和 Value2
mov eax, ebx
mov ebx, ecx
不能用xchg 交换两个都在内存中的操作数.
与mov指令相同,xchg指令也不影响任何一个状态标志.就是说,xchg指令执行
后,EFLAGS寄存器中的内容与指令执行前相比保持不变.
::: 结论 :::
好了,你一路读了过来,希望你从文章中学到了些什么.如果我能得到文章的反馈,
我会很高兴做的更多-只要还有人读它们!;)
现在...开心一下.做做下面的测试.如果你愿意的话,将答案发给我!
::: 测试自己!! :::
如果有兴趣,将你的答案通过OSIX的PM将你的答案发给我,我会告诉你你做的怎么
样.(仅对新手o!)
1.在问题的每一部分中,假定了给定的mov执行的"执行前"值.请给出要求的"执行后"值./*好运!*/
执行前-指令-执行后
(a) BX: FF 75
- mov bx, cx- BX, CX
CX: 01 A2
(b) AX: 01 A2- mov ax, 100- AX
(c) AX: 01 4B- mov ah, 0 AX- AX
(d) ECX: 00 00 00 00- mov ecx, 128- ECX
(e) AL: 64- mov al, -1- AL
祝 好!
Trope
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!